Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion test/js/bun/spawn/spawn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>).catch(() => {});
let caught: any;
try {
await proc.stdin!.end();
Expand Down
66 changes: 65 additions & 1 deletion test/js/web/intl/intl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Comment thread
robobun marked this conversation as resolved.
w.onmessage = e => { resolve(e.data); w.terminate(); };
w.onerror = reject;
w.postMessage(gate);
}));
Comment thread
robobun marked this conversation as resolved.
// 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");
Comment thread
robobun marked this conversation as resolved.
expect(exitCode).toBe(0);
});
Loading