diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index b7ca822369da..fc2182d56e46 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -7,7 +7,7 @@ // -lto variants built with ThinLTO (per-module summaries for cross-language // importing), and the Windows ICU data table filtered + per-item zstd // compressed (lazily decompressed via bun_icu_decompress.cpp). -export const WEBKIT_VERSION = "4895f45dfbd0d1226c4d41799887bc0ecb9f341b"; +export const WEBKIT_VERSION = "autobuild-preview-pr-297-3946a08b"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/test/js/bun/jsc/parser-deep-nesting.test.ts b/test/js/bun/jsc/parser-deep-nesting.test.ts new file mode 100644 index 000000000000..44750be42438 --- /dev/null +++ b/test/js/bun/jsc/parser-deep-nesting.test.ts @@ -0,0 +1,45 @@ +// JSC parser: once the native stack check fires on a deeply nested object/array +// literal, save-point backtracking (assignment-expression -> destructuring +// pattern -> member expression) used to clear the error and retry at every +// nesting level, turning an O(depth) failure into an exponential reparse loop +// that hung with unbounded memory growth. Now the first overflow is sticky and +// eval() rejects with RangeError in constant time regardless of depth. +import { expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; + +for (const [name, open, close] of [ + ["object literal", "{a:", "}"], + ["array literal", "[", "]"], +] as const) { + test(`deeply nested ${name} passed to eval() throws RangeError instead of hanging`, async () => { + // 50000 is far beyond the stack limit on every platform and build config; + // before the fix this hung forever (killed by the spawn timeout below). + const depth = 50000; + const fixture = ` + const src = "const y = " + Buffer.alloc(${depth * open.length}, ${JSON.stringify(open)}).toString() + + "1" + Buffer.alloc(${depth * close.length}, ${JSON.stringify(close)}).toString() + ";"; + try { + eval(src); + console.log("no-throw"); + } catch (e) { + console.log(e.constructor.name + ": " + e.message); + } + `; + + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", fixture], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + timeout: 20_000, + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect({ stdout: stdout.trim(), stderr, exitCode, signalCode: proc.signalCode }).toEqual({ + stdout: "RangeError: Maximum call stack size exceeded.", + stderr: "", + exitCode: 0, + signalCode: null, + }); + }, 30_000); +}