Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
70591cb
jsc: expose Node.js-style primordials as link-time constants
robobun Jul 25, 2026
795d638
build: point WEBKIT_VERSION at preview release for oven-sh/WebKit#341
robobun Jul 25, 2026
c244774
shorten primordials comments
robobun Jul 25, 2026
e56d298
test(primordials): gate tamper via env, keep harness on captured buil…
robobun Jul 25, 2026
1a9e9d4
ci: retrigger
robobun Jul 25, 2026
e3f28fe
test(primordials): poison getter-backed primordials and assert tamper…
robobun Jul 25, 2026
8ea828c
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 25, 2026
e4b4873
primordials: audit binding, generated typedefs, exhaustive tamper tests
dylan-conway Jul 25, 2026
f22c668
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 25, 2026
5173759
bump WebKit preview pin to d0f433e1
dylan-conway Jul 25, 2026
f5a9397
primordials: oxlint-disable in generated .d.ts, tamper canary in exha…
robobun Jul 25, 2026
b8e0b84
primordials: address review nits in internal-for-testing + tamper suite
robobun Jul 25, 2026
8af6966
test(primordials): wire worker exit handler so a crash surfaces immed…
robobun Jul 25, 2026
d7c3488
test(primordials): capture Reflect.set in prelude; reportAndExit in p…
robobun Jul 25, 2026
9abbbf9
Full Node.js primordials parity, generated
dylan-conway Jul 26, 2026
f7db891
bump WebKit preview pin to 39d7bcfd
dylan-conway Jul 26, 2026
cee3704
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 26, 2026
f08a7ea
primordials: address review feedback
dylan-conway Jul 26, 2026
46ae65d
primordials: keep Node's explicit Safe* constructors
dylan-conway Jul 26, 2026
e3a4639
Merge remote-tracking branch 'origin/main' into farm/caec3ad2/primord…
robobun Aug 3, 2026
2b93ac8
build: restore webkitTestFFIPath export dropped in merge
robobun Aug 3, 2026
22d75df
bump WebKit preview pin to 01de5fd4
robobun Aug 3, 2026
c6a0d3d
Merge remote-tracking branch 'origin/main' into farm/caec3ad2/primord…
robobun Aug 3, 2026
f153789
primordials: fix CI lint + Float16Array inspect crash; regenerate
dylan-conway Aug 3, 2026
75292f8
bump WebKit preview pin to 291ee11e
dylan-conway Aug 3, 2026
a9b486e
Merge origin/main into farm/caec3ad2/primordials-link-time-constants
robobun Aug 16, 2026
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
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 = "549170099226f816a4b204ea1d8fa102fb79eefa";
export const WEBKIT_VERSION = "autobuild-preview-pr-341-a4e834cb";

/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
125 changes: 125 additions & 0 deletions src/codegen/generate-primordials-dts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Generates src/js/primordials.d.ts from JavaScriptCore's JSCPrimordials.h so
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
// the $-prefixed primordial link-time constants stay in sync with the engine.
//
// bun src/codegen/generate-primordials-dts.ts [path/to/JSCPrimordials.h]
//
// Without an argument the header is located from $BUN_WEBKIT_PATH or vendor/WebKit.
Comment thread
robobun marked this conversation as resolved.
Outdated

import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";

const root = resolve(import.meta.dir, "../..");
const output = join(root, "src/js/primordials.d.ts");

function findHeader(): string {
const explicit = process.argv[2];
const candidates = explicit
? [explicit]
: [process.env.BUN_WEBKIT_PATH, join(root, "vendor/WebKit")]
.filter((dir): dir is string => !!dir)
.map(dir => join(dir, "Source/JavaScriptCore/runtime/JSCPrimordials.h"));
for (const candidate of candidates) if (existsSync(candidate)) return candidate;
throw new Error(`JSCPrimordials.h not found. Tried: ${candidates.join(", ")}`);
}

// Instance/namespace type for each JSC_FOREACH_PRIMORDIAL_<Holder> table.
const holderTypes: Record<string, string> = {
ObjectPrototype: "Object",
ObjectConstructor: "ObjectConstructor",
FunctionPrototype: "Function",
ArrayPrototype: "Array<any>",
ArrayConstructor: "ArrayConstructor",
StringPrototype: "String",
StringConstructor: "StringConstructor",
RegExpPrototype: "RegExp",
SymbolPrototype: "Symbol",
SymbolConstructor: "SymbolConstructor",
BigIntPrototype: "BigInt",
BigIntConstructor: "BigIntConstructor",
PromisePrototype: "Promise<any>",
PromiseConstructor: "PromiseConstructor",
IteratorPrototype: "IteratorObject<any>",
IteratorConstructor: "typeof Iterator",
ArrayIteratorPrototype: "ArrayIterator<any>",
StringIteratorPrototype: "StringIterator<any>",
MapIteratorPrototype: "MapIterator<any>",
SetIteratorPrototype: "SetIterator<any>",
RegExpStringIteratorPrototype: "RegExpStringIterator<any>",
IteratorHelperPrototype: "IteratorObject<any>",
WrapForValidIteratorPrototype: "IteratorObject<any>",
AsyncIteratorPrototype: "AsyncIteratorObject<any>",
WeakRefPrototype: "WeakRef<any>",
FinalizationRegistryPrototype: "FinalizationRegistry<any>",
GlobalFunctions: "typeof globalThis",
BooleanPrototype: "Boolean",
BooleanConstructor: "BooleanConstructor",
DatePrototype: "Date",
DateConstructor: "DateConstructor",
ErrorPrototype: "Error",
ErrorConstructor: "ErrorConstructor",
MapPrototype: "Map<any, any>",
MapConstructor: "MapConstructor",
NumberPrototype: "Number",
NumberConstructor: "NumberConstructor",
SetPrototype: "Set<any>",
SetConstructor: "SetConstructor",
WeakMapPrototype: "WeakMap<any, any>",
WeakMapConstructor: "WeakMapConstructor",
WeakSetPrototype: "WeakSet<any>",
WeakSetConstructor: "WeakSetConstructor",
JSArrayBufferPrototype: "ArrayBuffer",
JSArrayBufferConstructor: "ArrayBufferConstructor",
TypedArrayPrototype: "Uint8Array",
TypedArrayConstructor: "Uint8ArrayConstructor",
DataViewPrototype: "DataView",
MathObject: "Math",
JSONObject: "JSON",
ReflectObject: "typeof Reflect",
AtomicsObject: "Atomics",
};

const headerPath = findHeader();
const header = readFileSync(headerPath, "utf8");

const lines: string[] = [];
let count = 0;
for (const table of header.matchAll(/#define JSC_FOREACH_PRIMORDIAL_(\w+)\(V\)((?:\s*\\?\n?\s*V\([^\n]*)*)/g)) {
const holder = table[1];
if (holder === "NAME" || holder === "HOLDER" || holder.endsWith("_HOLDER")) continue;
const holderType = holderTypes[holder];
if (!holderType) throw new Error(`No TypeScript holder type registered for JSC_FOREACH_PRIMORDIAL_${holder}`);
for (const entry of table[2].matchAll(/V\(\s*(\w+),\s*(?:"([^"]+)"|(\w+)),\s*(\w+)\s*\)/g)) {
const [, name, stringKey, symbolKey, kind] = entry;
const key = stringKey !== undefined ? JSON.stringify(stringKey) : `typeof Symbol.${symbolKey}`;
const helper = kind === "Getter" || kind === "SymbolGetter" ? "PrimordialGetter" : "PrimordialMethod";
lines.push(`declare const $${name}: ${helper}<${holderType}, ${key}>;`);
count++;
}
}
if (count < 400)
throw new Error(`Parsed only ${count} primordials from ${headerPath}; the table format may have changed`);

const body = `// GENERATED FILE — do not edit. Regenerate with: bun src/codegen/generate-primordials-dts.ts
//
// Tamper-proof references to the original ECMAScript builtins, exposed by JSC
// (JSCPrimordials.h) as link-time constants and named after Node.js's primordials.
// Prototype methods and getters take the receiver via .$call/.$apply:
//
// $ArrayPrototypePush.$call(array, value);
// $MapPrototypeGetSize.$call(map);
// $ObjectDefineProperty(target, key, descriptor);
Comment thread
robobun marked this conversation as resolved.
Outdated

type PrimordialMethod<Holder, Key extends PropertyKey> = Holder extends Record<Key, infer Method>
? Method extends (...args: infer Args) => infer Return
? (this: Holder, ...args: Args) => Return
: Function
: Function;
type PrimordialGetter<Holder, Key extends PropertyKey> = Holder extends Record<Key, infer Value>
? (this: Holder) => Value
: Function;

${lines.join("\n")}
`;

writeFileSync(output, body);
console.log(`Wrote ${count} primordial declarations to ${output} (from ${headerPath})`);
54 changes: 54 additions & 0 deletions src/js/internal-for-testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,60 @@
);
export const Dequeue = require("internal/fifo");

// Link-time-constant primordials probe; one entry per holder kind in JSCPrimordials.h.
export const primordials = {
run(arrayLike: unknown[], string: string, mapLike: Map<unknown, unknown>, u8: Uint8Array, regexp: RegExp) {
return {
ArrayPrototypePush: $ArrayPrototypePush.$call(arrayLike, 1, 2),
ArrayPrototypeSlice: $ArrayPrototypeSlice.$call(arrayLike, 0),
ArrayPrototypeSymbolIterator: $ArrayPrototypeSymbolIterator.$call(arrayLike).next().value,
StringPrototypeSlice: $StringPrototypeSlice.$call(string, 1, 3),
StringPrototypeSplit: $StringPrototypeSplit.$call(string, ""),
ObjectKeys: $ObjectKeys({ a: 1, b: 2 }),
ObjectDefineProperty: $ObjectDefineProperty({}, "x", { value: 42 }).x,
FunctionPrototypeBind: $FunctionPrototypeBind.$call(function (this: number, y: number) {
return this + y;
}, 5)(3),
RegExpPrototypeTest: $RegExpPrototypeTest.$call(regexp, string),
RegExpPrototypeGetSource: $RegExpPrototypeGetSource.$call(regexp),
MapPrototypeGet: $MapPrototypeGet.$call(mapLike, "k"),
MapPrototypeGetSize: $MapPrototypeGetSize.$call(mapLike),
DateNow: typeof $DateNow(),
NumberIsInteger: $NumberIsInteger(3),
MathMax: $MathMax(1, 5, 3),
ReflectOwnKeys: $ReflectOwnKeys({ a: 1 }),
JSONStringify: $JSONStringify({ a: 1 }),
TypedArrayPrototypeGetLength: $TypedArrayPrototypeGetLength.$call(u8),
TypedArrayPrototypeSubarray: $TypedArrayPrototypeGetLength.$call($TypedArrayPrototypeSubarray.$call(u8, 1, 3)),
DataViewPrototypeGetByteLength: $DataViewPrototypeGetByteLength.$call(
new DataView($TypedArrayPrototypeGetBuffer.$call(u8)),
),
PromiseResolve: $PromiseResolve.$call($Promise, 1) instanceof $Promise,
};
},
refs() {
return {
ArrayPrototypePush: $ArrayPrototypePush,
StringPrototypeSlice: $StringPrototypeSlice,
ObjectDefineProperty: $ObjectDefineProperty,
MapPrototypeGet: $MapPrototypeGet,
MathMax: $MathMax,
ReflectOwnKeys: $ReflectOwnKeys,
TypedArrayPrototypeGetLength: $TypedArrayPrototypeGetLength,
};
},

Check warning on line 269 in src/js/internal-for-testing.ts

View check run for this annotation

Claude / Claude Code Review

primordials.refs() is dead code left behind by the e4b48731 test rewrite

`primordials.refs()` is now dead code — its only caller (`test/js/bun/util/primordials.test.ts` at 8ea828c8, line 49) was removed when e4b48731 rewrote the test file to use `primordials.audit()` exclusively, and `audit()` already returns `.value` for every primordial so `refs()` is a strict subset with no remaining purpose. Per REVIEW.md ("Delete dead code in the same PR that makes it dead … helpers whose last caller you rewired"), delete the `refs()` method.
Comment thread
robobun marked this conversation as resolved.
Outdated
// Materializes every primordial and returns one { name, holder, kind, key, value, available }
// row per JSCPrimordials.h entry, straight from JSC.
Comment thread
robobun marked this conversation as resolved.
Outdated
audit: $newCppFunction("PrimordialsAudit.cpp", "Bun__primordialsAudit", 0) as () => Array<{
name: string;
holder: string;
kind: "Method" | "Getter" | "SymbolMethod" | "SymbolGetter";
key: string | symbol;
value: Function;
available: boolean;
}>,
};

// Userland access to node-internal modules for vendored node tests that
// declare `// Flags: --expose-internals` (served via the require interceptor
// in test/js/node/test/common/index.js). Static requires only — the builtin
Expand Down
Loading
Loading