Skip to content
2 changes: 1 addition & 1 deletion scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* for local mode. Override via `--webkit-version=<hash>` to test a branch.
* From https://github.com/oven-sh/WebKit releases.
*/
export const WEBKIT_VERSION = "cf1b36ec8703d8e87436094d21d478d358c7d886";
export const WEBKIT_VERSION = "autobuild-preview-pr-623-e1831791";

/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/BunProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ static JSValue constructProcessReleaseObject(VM& vm, JSObject* processObject)
auto* release = JSC::constructEmptyObject(globalObject);

release->putDirect(vm, vm.propertyNames->name, jsOwnedString(vm, String("node"_s)), 0); // maybe this should be 'bun' eventually
putDirectNamed(vm, release, "sourceUrl"_s, jsOwnedString(vm, WTF::String(std::span { Bun__githubURL, strlen(Bun__githubURL) })));
putDirectNamed(vm, release, "sourceUrl"_s, jsOwnedString(vm, String::fromLatin1(Bun__githubURL)));
putDirectNamed(vm, release, "headersUrl"_s, jsOwnedString(vm, String("https://nodejs.org/download/release/v" REPORTED_NODEJS_VERSION "/node-v" REPORTED_NODEJS_VERSION "-headers.tar.gz"_s)));

RETURN_IF_EXCEPTION(scope, {});
Expand Down
83 changes: 83 additions & 0 deletions test/js/bun/jsc/webkit-upgrade-50320fd3b3.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { describe, expect, test } from "bun:test";

// Coverage for the WebKit 50320fd3b3 sync (oven-sh/WebKit#623). The first two cases pin
// an observable difference between the old and the new JavaScriptCore. The third runs the
// shapes the new B3 lowering matches through the JIT tiers.

describe.concurrent("WebKit 50320fd3b3 upgrade", () => {
test("ArrayBuffer.prototype.resize range-checks the length before the detached check (223bd0faee)", () => {
const detached = new ArrayBuffer(8, { maxByteLength: 16 });
detached.transfer();
expect(() => detached.resize(-1)).toThrow(RangeError);
expect(() => detached.resize(2 ** 53)).toThrow(RangeError);
// A length that passes ToIndex still reaches the detached check.
expect(() => detached.resize(8)).toThrow(TypeError);

const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
expect(() => buffer.resize(1e20)).toThrow(RangeError);
expect(() => buffer.resize(Infinity)).toThrow(RangeError);
expect(() => buffer.resize(-1)).toThrow(RangeError);
buffer.resize(16);
expect(buffer.byteLength).toBe(16);
});

test("SharedArrayBuffer.prototype.grow rejects a length that is not an index (223bd0faee)", () => {
const shared = new SharedArrayBuffer(8, { maxByteLength: 16 });
expect(() => shared.grow(1e20)).toThrow(RangeError);
expect(() => shared.grow(2 ** 53)).toThrow(RangeError);
expect(() => shared.grow(-1)).toThrow(RangeError);
shared.grow(16);
expect(shared.byteLength).toBe(16);
});

test("a multiply with a negated operand keeps its value, sign of zero included (41ec81351b)", () => {
const negLeft = (w: number, r: number) => -w * r;
const negRight = (w: number, r: number) => w * -r;
const negBoth = (w: number, r: number) => -w * -r;
// The negation has two users here, so the multiply cannot absorb it.
const sharedNeg = (w: number, r: number) => {
const n = -w;
return n * r + n;
};
const negLeftInt = (w: number, r: number) => (-w * r) | 0;

// w, r, -w * r, w * -r, -w * -r
const cases: [number, number, number, number, number][] = [
[0, 3, -0, -0, 0],
[-0, 3, 0, 0, -0],
[0, -3, 0, 0, -0],
[-0, -3, -0, -0, 0],
[0, -0, 0, 0, -0],
[0, Infinity, NaN, NaN, NaN],
[2, Infinity, -Infinity, -Infinity, Infinity],
[NaN, 3, NaN, NaN, NaN],
[1.5, 2.5, -3.75, -3.75, 3.75],
[-1.5, 2.5, 3.75, 3.75, -3.75],
[Number.MAX_VALUE, 2, -Infinity, -Infinity, Infinity],
];
// w, r, (-w * r) | 0
const intCases: [number, number, number][] = [
[3, 7, -21],
[-3, 7, 21],
[0x7fffffff, 2, 2],
[-0x80000000, 2, 0],
[0x10000, 0x10000, 0],
];

const mismatches: string[] = [];
const check = (actual: number, expected: number, what: string, w: number, r: number) => {
if (!Object.is(actual, expected) && mismatches.length < 10)
mismatches.push(`${what}(${w}, ${r}): got ${actual}, expected ${expected}`);
};
for (let i = 0; i < 1e4; ++i) {
for (const [w, r, left, right, both] of cases) {
check(negLeft(w, r), left, "negLeft", w, r);
check(negRight(w, r), right, "negRight", w, r);
check(negBoth(w, r), both, "negBoth", w, r);
check(sharedNeg(w, r), left + -w, "sharedNeg", w, r);
}
for (const [w, r, expected] of intCases) check(negLeftInt(w, r), expected, "negLeftInt", w, r);
}
expect(mismatches).toEqual([]);
});
});
60 changes: 60 additions & 0 deletions test/js/bun/jsc/webkit-upgrade-ccdcb8a026.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";

// Coverage for the WebKit ccdcb8a026 sync (oven-sh/WebKit#614). Each case pins an
// observable difference between the old and the new JavaScriptCore. The last case
// exercises the GlobalObjectMethodTable::moduleTypeIsAllowed slot the range adds: a
// host-defined import attribute type must still reach Bun's module loader.

describe.concurrent("WebKit ccdcb8a026 upgrade", () => {
test("Array.prototype.toSpliced throws RangeError for a new length of 2^53 - 1 (c8c37314ee)", () => {
expect(() => Array.prototype.toSpliced.call({ length: Infinity }, 0, 0)).toThrow(RangeError);
// One past 2^53 - 1 is still the TypeError from step 10 of the spec algorithm.
expect(() => Array.prototype.toSpliced.call({ length: 2 ** 53 - 1 }, 0, 0, 1)).toThrow(TypeError);
});

test("Atomics.isLockFree uses ToIntegerOrInfinity instead of wrapping to int32 (41294576ac)", () => {
expect(Atomics.isLockFree(4)).toBe(true);
expect(Atomics.isLockFree(4294967297)).toBe(false);
expect(Atomics.isLockFree(2 ** 32 + 4)).toBe(false);
});

test("RegExp.escape keeps supplementary code points whose low 16 bits look like syntax characters (b44e00d2f0)", () => {
// U+2002A has 0x002A ('*') in its low 16 bits and U+20009 has 0x0009 (TAB).
expect(RegExp.escape("\u{2002A}")).toBe("\u{2002A}");
expect(RegExp.escape("\u{20009}")).toBe("\u{20009}");
expect(RegExp.escape("*")).toBe("\\*");
});

test("a strict async generator body does not tail-call its return expression (4fa7b55ae4)", async () => {
async function* g() {
"use strict";
return Promise.resolve(42);
}
const result = await g().next();
expect(result).toEqual({ value: 42, done: true });
});

test("host-defined import attribute types still load through Bun's module loader (64168e4d90)", async () => {
using dir = tempDir("wk-module-type", {
"data.toml": `name = "bun"\n`,
"note.txt": `hello`,
"entry.mjs": `
import data from "./data.toml" with { type: "toml" };
import note from "./note.txt" with { type: "text" };
const dynamic = await import("./data.toml", { with: { type: "toml" } });
process.stdout.write(JSON.stringify({ name: data.name, note, dynamic: dynamic.default.name }));
`,
});
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({ name: "bun", note: "hello", dynamic: "bun" });
expect(exitCode).toBe(0);
});
});
Loading