Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
495c6a7
Remove the .zig porting-reference sources
cirospaciari Jun 23, 2026
24e728b
codegen: drop on-disk .zig existence checks
cirospaciari Jun 23, 2026
0356580
Drop the Ban Words workflow step and 'banned' script
cirospaciari Jun 23, 2026
432c094
test: retarget the broken-pipe grep to parser.rs; drop the Zig-tests …
cirospaciari Jun 23, 2026
c69b6b3
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 23, 2026
4981c7c
codegen: restore resolved $zig() paths via a static table
robobun Jun 23, 2026
bf3d661
Merge remote-tracking branch 'origin/main' into ciro/rm-zig-sources
robobun Jun 23, 2026
ad42b11
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 23, 2026
a6be699
Sweep remaining scripts/ references to deleted .zig sources
robobun Jun 23, 2026
4616dc0
glob-sources: declare dev_server/mod.rs as a bakeRuntime input
robobun Jun 23, 2026
b437502
Sweep remaining dead Zig artifacts and doc references
robobun Jun 23, 2026
9e7d0a8
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 23, 2026
92ddac7
ci: retrigger (darwin-26-aarch64 artifact download timeout on #64230)
robobun Jun 23, 2026
06a9ab7
Merge remote-tracking branch 'origin/main' into ciro/rm-zig-sources
cirospaciari Jun 23, 2026
7055cd2
Fold all remaining Zig follow-ups into this PR
robobun Jun 23, 2026
cf5b836
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 23, 2026
07ba891
cppbind.ts: fix dangling resultFilePath reference in CI log line
robobun Jun 23, 2026
51831b6
Remove stale generated .zig files accidentally committed in 07ba891
robobun Jun 23, 2026
d75696d
Address review: perf-trace script correctness, half-edited comments, …
robobun Jun 23, 2026
a03841e
Restore .gitignore entries for the legacy Generated*.zig outputs
robobun Jun 23, 2026
8034036
Merge remote-tracking branch 'origin/main' into ciro/rm-zig-sources
robobun Jun 23, 2026
a321e6a
Merge main for #32608 s3 leak fix; address remaining review nits
robobun Jun 23, 2026
f32c694
codegen: remove dead Zig-emission machinery from cppbind/bindgenv2/sh…
robobun Jun 24, 2026
b7322e0
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 24, 2026
866aafb
Sweep four more dead-Zig stragglers
robobun Jun 24, 2026
b922970
cppbind: port [[ZIG_EXPORT]] validation into generateRustFn; finish d…
robobun Jun 24, 2026
69a13b2
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 24, 2026
f888622
Fix remaining half-edited comments; drop dead .gitignore/prose entries
robobun Jun 24, 2026
af58a93
Fix three more half-edit/dead-code residuals
robobun Jun 24, 2026
0852a94
build-prefixes.js correctness: drop is_flex_2009 shim; delete generat…
robobun Jun 24, 2026
087a307
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 24, 2026
5d296d4
generate-perf-trace-events.sh: use awk NF instead of grep -v for blan…
robobun Jun 24, 2026
47fb444
Address three review residuals: StandaloneModuleGraph half-edit, gene…
robobun Jun 24, 2026
ef708c3
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 24, 2026
19d0be3
targets.rs: re-pad Features bitflags to match build-prefixes.js output
robobun Jun 24, 2026
b85c825
mime_type: finish the generate-compact-string-table comment cleanup
robobun Jun 24, 2026
a019c6f
build-prefixes.js: match committed targets.rs exactly; Cargo.toml hal…
robobun Jun 24, 2026
0018bdd
Fix ReleaseFast and image/README.md half-edits
robobun Jun 24, 2026
c4f326a
perf/generated_perf_trace_events.rs: align header to script output
robobun Jun 24, 2026
a9b50b7
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 24, 2026
099e0f6
Docs: correct image/README system_backend; fix zig fences in STREAMS/…
robobun Jun 24, 2026
a23032c
v8 CLAUDE/AGENTS.md: rewrite the V8API code fence to Rust
robobun Jun 24, 2026
31a99ae
Docs: sweep remaining Zig prose/fences in b437502b-touched READMEs
robobun Jun 24, 2026
3c136e7
v8 CLAUDE/AGENTS.md: fix stale src\bun.js\ path in dumpbin line
robobun Jun 24, 2026
ee6fe62
Docs: rewrite last two ```zig fences to Rust
robobun Jun 24, 2026
fb32581
SKILL.md: sweep remaining jsc.JSRef/jsc.Strong/Zig cross-references
robobun Jun 24, 2026
962393f
js2native: rename $zig/$newZigFunction → $rust/$newRustFunction
Jarred-Sumner Jun 25, 2026
46fff1c
Fix half-edits and wrong trait path from 962393f5
robobun Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .claude/commands/upgrade-webkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ To do that:

Things to check for a successful upgrade:

- Did Source/JavaScriptCore/runtime/JSType.h change? The enum values must align with Bun's mirror in src/jsc/JSType.rs (src/jsc/JSType.zig is a non-compiled porting reference, not the live code).
- Did Source/JavaScriptCore/runtime/JSType.h change? The enum values must align with Bun's mirror in src/jsc/JSType.rs.
Comment thread
claude[bot] marked this conversation as resolved.
- Were there any changes to the WebCore code generator? If there are C++ compilation errors, check for differences in the generated reference code in vendor/WebKit/Source/WebCore/bindings/scripts/test/JS/
- If the merge touched the fork's .github/workflows, the release tarball names must still match prebuiltSuffix() in scripts/build/deps/webkit.ts
2 changes: 1 addition & 1 deletion .claude/docs/landing-prs.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ Companion to the "Landing PRs: What Bun Reviewers Catch" section in CLAUDE.md. T
- **Audit the full diff for accidental ride-alongs.** Submodule pointer bumps, lockfile churn from rebases, regenerated snapshots, formatter churn on untouched code, stash leakage. After every merge/rebase with main, re-diff against main: conflict resolution can silently resurrect deleted code or drop your own headline fix while keeping its test (a "one-line test tweak" commit once touched 33 files and reverted the entire production fix). Every file in the diff must be explainable from the PR's stated purpose.
- **The PR description is the permanent squash-commit message — keep it true.** State the root cause and make the exact fixing line identifiable apart from refactoring ("Which line was the fix?"). Name the verifying tests and state they fail on the unfixed build. "Fixes #N" must match the issue's actual repro; a partial fix says so. Re-sync title/description whenever review reworks the change. Every hunk needs an articulable one-sentence justification — pre-empt it in the description or a code comment for anything a reviewer can't explain from context. On large mechanical diffs, leave self-review comments pointing at the load-bearing hunks. Never delete unrelated code, others' TODOs, or debug tooling in passing — deletions your change orphans are required (see "Delete dead code"), but each is intentional and named.
- **Treat every review suggestion — especially from bots — as an unverified hypothesis.** Reproduce or check it against actual API semantics before applying or dismissing. Apply real findings; decline wrong ones in-thread with checkable evidence (file:line, run transcripts) — evidence-backed rebuttals close threads, bare dismissals don't. Never blanket-apply (blindly-applied suggestions have reintroduced known ASAN failures), never resolve threads silently in bulk (a bulk-resolve once swallowed a genuine correctness bug). When a reviewer flags a pattern once, sweep and fix every instance — Jarred leaves one substantive comment then "ditto" on each clone; fixing only the commented line guarantees another round.
- **Green CI on every platform is a hard precondition.** Maintainers file changes-requested reviews consisting solely of "CI is failing". Confirm you changed the implementation that ships (a complete fix to a dormant .zig reference file got the one-line review "redo this PR in Rust"). Regenerate checked-in codegen outputs — again after every rebase. Every failure on your branch but not on main is yours to root-cause; "probably a flake" requires a link to the same failure on main. Check per-job results, not the aggregate icon, and confirm CI actually executed your tests — path filters silently skip them.
- **Green CI on every platform is a hard precondition.** Maintainers file changes-requested reviews consisting solely of "CI is failing". Regenerate checked-in codegen outputs — again after every rebase. Every failure on your branch but not on main is yours to root-cause; "probably a flake" requires a link to the same failure on main. Check per-job results, not the aggregate icon, and confirm CI actually executed your tests — path filters silently skip them.
- **One concern per PR, scoped to the narrowest change that fixes it.** Fixing every instance of the same bug class is ONE concern (see "Fix the whole class"); drive-by refactors, style cleanups of adjacent code, and vendored upgrades are not. If one part triggers design debate mid-review, carve it out so the uncontroversial part merges. Diff size itself is grounds for changes-requested.
- **Pre-existing bugs surfaced by review: acknowledge, scope, track.** Never silently ignore, never silently widen your diff. State the mechanism in-thread, note your PR doesn't change it, file a tracking issue — "out of scope" without a tracker is not accepted. Exception: if it's the exact bug class your PR claims to eliminate, fix all instances in the same PR ("pre-existing, will follow up" for the same crash class gets "no, fix it.").
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,6 @@ if (!filePath) {
process.exit(0);
}

function formatZigFile() {
try {
// Format the Zig file
const result = spawnSync("vendor/zig/zig.exe", ["fmt", filePath], {
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
encoding: "utf-8",
});

if (result.error) {
console.error(`Failed to format ${filePath}: ${result.error.message}`);
process.exit(0);
}

if (result.status !== 0) {
console.error(`zig fmt failed for ${filePath}:`);
if (result.stderr) {
console.error(result.stderr);
}
process.exit(0);
}
} catch (error) {}
}

function formatTypeScriptFile() {
try {
// Format only — NO organize-imports plugin. That plugin strips imports
Expand All @@ -56,9 +33,7 @@ function formatTypeScriptFile() {
} catch (error) {}
}

if (ext === ".zig") {
formatZigFile();
} else if (
if (
[
".cjs",
".css",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,6 @@ useSystemBun = inlineEnv.get("USE_SYSTEM_BUN") ?? useSystemBun;
// Get the executable name (argv0)
const argv0 = basename(tokens[0], extname(tokens[0]));

// Check if it's zig or zig.exe
if (argv0 === "zig") {
// Filter out flags (starting with -) to get positional arguments
const positionalArgs = tokens.slice(1).filter(arg => !arg.startsWith("-"));

// Check if the positional args contain "build" followed by "obj"
if (positionalArgs.length >= 2 && positionalArgs[0] === "build" && positionalArgs[1] === "obj") {
denyWithReason("error: Use `bun bd` to build Bun and wait patiently");
}
}

// Disallow direct `rustfmt`: it doesn't read the workspace edition from
// Cargo.toml the way `cargo fmt` does, so its output can disagree with CI's
Expand Down
4 changes: 2 additions & 2 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-bash-zig-build.js"
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-bash-guard.js"
}
]
}
Expand All @@ -17,7 +17,7 @@
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-zig-format.js"
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-format.js"
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.
}
]
}
Expand Down
131 changes: 131 additions & 0 deletions .claude/skills/implementing-jsc-classes-rust/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
name: implementing-jsc-classes-rust
description: Creates JavaScript classes using Bun's Rust bindings generator (.classes.ts). Use when implementing new JS APIs in Rust with JSC integration, prototypes, or constructors.
---

# Bun's JavaScriptCore Class Bindings Generator

Bridge JavaScript and Rust through `.classes.ts` definitions and Rust implementations.

## Architecture

1. **JavaScript Interface Definition** (`.classes.ts` files)
2. **Rust Implementation** (`.rs` files)
3. **Generated Code** — `src/codegen/generate-classes.ts` emits C++ + Rust into `${BUN_CODEGEN_DIR}/generated_classes.rs`, `include!`d as `crate::generated_classes` in `bun_runtime`. Run `bun bd` to regenerate.

## Class Definition (.classes.ts)

```typescript
export default [
define({
name: "Glob",
construct: true,
finalize: true,
hasPendingActivity: true,
proto: {
scan: { fn: "scan", length: 1 },
match: { fn: "match", length: 1 },
},
}),
];
```

Options:

- `construct`: Has a public `new Foo()` constructor
- `finalize`: Needs cleanup beyond `Drop` (rarely — see Finalize below)
- `hasPendingActivity`: GC keep-alive while async work is in flight
- `proto`: Methods (`fn:`), getters (`getter: true`, optionally `cache: true`)
- `values: [...]`: WriteBarrier slots for JS values the native side holds (callbacks, buffers)

## Rust Implementation

```rust
use bun_jsc::{CallFrame, JSGlobalObject, JSValue, JsResult};
use std::sync::atomic::{AtomicUsize, Ordering};

#[bun_jsc::JsClass]
pub struct Glob {
pattern: Box<[u8]>,
has_pending_activity: AtomicUsize,
}

impl Glob {
pub fn constructor(global: &JSGlobalObject, frame: &CallFrame) -> JsResult<Box<Glob>> {
let arg = frame.argument(0);
let pattern = bun_core::String::from_js(arg, global)?.to_utf8_bytes().into();
Ok(Box::new(Glob { pattern, has_pending_activity: AtomicUsize::new(0) }))
}

#[bun_jsc::host_fn(method)]
pub fn r#match(&self, global: &JSGlobalObject, frame: &CallFrame) -> JsResult<JSValue> {
// ...
Ok(JSValue::TRUE)
}

pub fn has_pending_activity(&self) -> bool {
self.has_pending_activity.load(Ordering::SeqCst) > 0
}
}
```

### Canonical signatures

| Hook | Signature |
| ------------------- | ------------------------------------------------------------------------------------ |
| constructor | `pub fn constructor(global: &JSGlobalObject, frame: &CallFrame) -> JsResult<Box<Self>>` |
| method (`fn:`) | `pub fn name(&self, global: &JSGlobalObject, frame: &CallFrame) -> JsResult<JSValue>` |
| getter | `pub fn get_x(this: &Self, global: &JSGlobalObject) -> JsResult<JSValue>` |
| finalize | `pub fn finalize(self: Box<Self>)` — or omit; the blanket `JsFinalize` just drops |
| hasPendingActivity | `pub fn has_pending_activity(&self) -> bool` |

A missing or mis-typed hook is a **compile error** in `cargo check -p bun_runtime` — the generated code calls the inherent method directly.

## Hooking into the generated module

`#[bun_jsc::JsClass]` on the struct implements the `JsClass` trait (`to_js`, `from_js`, `from_js_direct`, `get_constructor`) by binding the C++ externs. Attribute knobs: `no_constructor`, `no_finalize`, `estimated_size`.

The codegen also emits a `js_$T` module with the cached-value accessors. Re-export it when you need `*_set_cached` / `*_get_cached` or `detach_ptr`:

```rust
pub use crate::generated_classes::js_Glob as js;
// or
bun_jsc::impl_js_class_via_generated!(Archive => crate::generated_classes::js_Archive);
```

The `js_$T` module surface:

```rust
pub fn from_js(value: JSValue) -> Option<NonNull<T>>;
pub fn from_js_direct(value: JSValue) -> Option<NonNull<T>>;
pub fn get_constructor(global: &JSGlobalObject) -> JSValue;
pub fn to_js(this: *mut T, global: &JSGlobalObject) -> JSValue; // ownership transfer
pub fn detach_ptr(value: JSValue);
// per cached getter / `values: [...]` entry:
pub fn <field>_set_cached(this_value: JSValue, global: &JSGlobalObject, value: JSValue);
pub fn <field>_get_cached(this_value: JSValue) -> Option<JSValue>;
```

## Finalize

Most classes need nothing — `#[bun_jsc::JsClass]` wires the blanket `JsFinalize` whose default is `drop(Box<Self>)`. Override only when you must release a JS handle or defer to a heap helper:

```rust
pub fn finalize(self: Box<Self>) {
bun_ptr::finalize_js_box(self, |this| this.this_value.with_mut(|v| v.finalize()));
}
```

Override with an **inherent** method, never `impl JsFinalize for T`.

## Holding JS values

Never store raw `JSValue` in a struct field. Declare a slot in `.classes.ts` (`values: ["callback"]` or a `cache: true` getter) and read/write it through `js::callback_set_cached(this_value, global, v)` / `js::callback_get_cached(this_value)`. The slot is a `WriteBarrier` visited by the GC, so the value stays alive without a `Strong`.

## Reference implementations

- `src/runtime/api/glob.rs` + `Glob.classes.ts` — constructor, methods, `hasPendingActivity`, default finalize
- `src/runtime/api/cron.rs` + `cron.classes.ts` — `noConstructor`, cached getter, `values: [...]`, custom finalize
- `src/runtime/image/Image.rs:56` — the `pub use crate::generated_classes::js_Image as js;` one-liner
- `src/jsc/host_fn.rs` — the host-fn adapters the codegen dispatches through
- `src/jsc_macros/lib.rs` — `#[bun_jsc::JsClass]` proc-macro source
Comment thread
robobun marked this conversation as resolved.
Loading
Loading