diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index e50f02454d23..647a52d3d942 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -6,9 +6,10 @@ // oven-sh/WebKit main: macOS + Windows artifacts cross-compiled on Linux, // -lto variants built with ThinLTO (per-module summaries for cross-language // importing), every x64 at the nehalem floor (no separate -baseline variant), -// typed-array constructor ClassInfo kept address-unique under LTO, and the -// Windows ICU data table filtered + per-item zstd compressed. -export const WEBKIT_VERSION = "c9296e353e365ecf0de82f273bb0a88a3df465be"; +// typed-array constructor ClassInfo kept address-unique under LTO, the +// Windows ICU data table filtered + per-item zstd compressed, and the eager +// timezone prewarm in VM::VM skipped under USE(BUN_JSC_ADDITIONS). +export const WEBKIT_VERSION = "9bded08f48f30b0fe37fdf5424a89b9bcae4349f"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/test/js/bun/spawn/spawn.test.ts b/test/js/bun/spawn/spawn.test.ts index 72dc82f28737..7ef078fe6ce5 100644 --- a/test/js/bun/spawn/spawn.test.ts +++ b/test/js/bun/spawn/spawn.test.ts @@ -460,7 +460,12 @@ for (let [gcTick, label] of [ stdout: "ignore", stderr: "ignore", }); - proc.stdin!.write(Buffer.alloc(16 * 1024 * 1024, 0x41)); + // write() may itself reject with EPIPE if the child has already exited by the + // time the first pipe-buffer flush runs; keep it unawaited so end() still has + // buffered data to drain, but swallow its rejection so it cannot surface as an + // unhandled rejection and fail the test. + const wrote = proc.stdin!.write(Buffer.alloc(16 * 1024 * 1024, 0x41)); + if (wrote && typeof (wrote as any).catch === "function") (wrote as Promise).catch(() => {}); let caught: any; try { await proc.stdin!.end(); diff --git a/test/js/web/intl/intl.test.ts b/test/js/web/intl/intl.test.ts index 86dc3758a8e8..e03541db5eda 100644 --- a/test/js/web/intl/intl.test.ts +++ b/test/js/web/intl/intl.test.ts @@ -10,7 +10,7 @@ // links the unmodified libicudata.a. import { describe, expect, test } from "bun:test"; -import { isLinux } from "harness"; +import { bunEnv, bunExe, isLinux } from "harness"; // Snapshots are CLDR-version-specific. Only check them where Bun bundles the // ICU they were generated against (Linux); macOS uses Apple's libicucore and @@ -307,3 +307,67 @@ describe("exhaustive locale sweep (every compressed item)", () => { } }); }); + +// The IANA timezone table and host-zone display-name cache are filled lazily on +// first Date / Intl access rather than inside VM::VM, so the first access can +// race across Workers that each construct their own VM. Exercise that race in a +// fresh process where nothing has warmed the process-wide table yet: every +// Worker parks on a shared barrier until all eight are ready, then they probe +// simultaneously. The main thread touches Date / Intl only after collecting the +// Worker results, so it cannot warm the cache ahead of them. +test.concurrent("timezone lazy-init is consistent across concurrent Workers", async () => { + const script = ` + const N = 8; + // gate[0]: count of workers that have reached the barrier. + // gate[1]: release flag; workers Atomics.wait on it until main notifies. + const gate = new Int32Array(new SharedArrayBuffer(8)); + const probe = () => ({ + zone: new Intl.DateTimeFormat().resolvedOptions().timeZone, + count: Intl.supportedValuesOf("timeZone").length, + date: new Date(0).toString(), + }); + const body = + "self.onmessage = e => {" + + " const gate = e.data;" + + " Atomics.add(gate, 0, 1);" + + " Atomics.wait(gate, 1, 0);" + + " postMessage((" + probe.toString() + ")());" + + "};"; + const url = URL.createObjectURL(new Blob([body])); + const results = Array.from({ length: N }, () => new Promise((resolve, reject) => { + const w = new Worker(url); + w.onmessage = e => { resolve(e.data); w.terminate(); }; + w.onerror = reject; + w.postMessage(gate); + })); + // Race the barrier against the Worker promises so a startup error surfaces + // instead of spinning until the outer test times out. + await Promise.race([ + (async () => { while (Atomics.load(gate, 0) < N) await Bun.sleep(0); })(), + Promise.all(results), + ]); + Atomics.store(gate, 1, 1); + Atomics.notify(gate, 1); + const worker = await Promise.all(results); + const main = probe(); + for (const r of [main, ...worker]) + if (r.zone !== worker[0].zone || r.count !== worker[0].count || r.date !== worker[0].date) + throw new Error("inconsistent: " + JSON.stringify(r) + " vs " + JSON.stringify(worker[0])); + console.log(JSON.stringify(worker[0])); + `; + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: { ...bunEnv, TZ: "America/New_York", LANG: "en_US.UTF-8", LC_ALL: "en_US.UTF-8" }, + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + const result = JSON.parse(stdout.trim()); + expect(result.zone).toBe("America/New_York"); + expect(result.count).toBeGreaterThan(400); + expect(result.date).toContain("GMT-0500"); + // The parenthesized long name exercises the host-zone display-name cache; pinning + // LANG above keeps this locale-independent across developer machines. + expect(result.date).toContain("Eastern Standard Time"); + expect(exitCode).toBe(0); +});