Skip to content

Commit 2b8ac69

Browse files
committed
add support for traceable collections and custom tracing in Rust JSG
1 parent 75386fb commit 2b8ac69

18 files changed

Lines changed: 2767 additions & 1264 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Be aware that workerd uses tcmalloc for memory allocation in the typical case. W
167167
- **`src/cloudflare/`** - Cloudflare-specific APIs (TypeScript)
168168
- **`src/node/`** - Node.js compatibility layer (TypeScript)
169169
- **`src/pyodide/`** - Python runtime support via Pyodide
170-
- **`src/rust/`** - Rust integration components
170+
- **`src/rust/`** - Rust integration components; see `src/rust/AGENTS.md` for the full macro reference and GC tracing guide
171171

172172
### Configuration System
173173

src/rust/AGENTS.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
| Crate | Purpose |
1010
| -------------------- | ------------------------------------------------------------------------------------------------------ |
1111
| `jsg/` | Rust JSG bindings: `Lock`, `Rc<T>`, `Resource`, `Struct`, `Type`, `Realm`, `FeatureFlags`, module registration; V8 handle types including typed arrays, `ArrayBuffer`, `ArrayBufferView`, `SharedArrayBuffer`, `BackingStore` |
12-
| `jsg-macros/` | Proc macros: `#[jsg_struct]`, `#[jsg_method]`, `#[jsg_resource]`, `#[jsg_oneof]`, `#[jsg_static_constant]` |
12+
| `jsg-macros/` | Proc macros: `#[jsg_struct]`, `#[jsg_method]`, `#[jsg_resource]`, `#[jsg_oneof]`, `#[jsg_static_constant]`, `#[jsg_constructor]` |
1313
| `jsg-test/` | Test harness (`Harness`) for JSG Rust bindings |
1414
| `api/` | Rust-implemented Node.js APIs; registers modules via `register_nodejs_modules()` |
1515
| `dns/` | DNS record parsing (CAA, NAPTR) via CXX bridge; legacy duplicate of `api/dns.rs`, pending removal |
@@ -25,11 +25,13 @@
2525
- **CXX bridge**: `#[cxx::bridge(namespace = "workerd::rust::<crate>")]` with companion `ffi.c++`/`ffi.h` files
2626
- **Namespace**: always `workerd::rust::*` except `python-parser``edgeworker::rust::python_parser`
2727
- **Errors**: `thiserror` for library crates; `jsg::Error` with `ExceptionType` for JSG-facing crates
28-
- **JSG resources**: `#[jsg_resource]` on struct + impl block; `#[jsg_method]` auto-converts `snake_case``camelCase`; methods with `&self`/`&mut self` become instance methods, methods without a receiver become static methods; `#[jsg_static_constant]` on `const` items exposes read-only numeric constants on both constructor and prototype (name kept as-is, no camelCase); resources integrate with GC via the `GarbageCollected` trait (auto-derived for `Rc<T>`, `WeakRc<T>`, `Option<Rc<T>>`, and `Nullable<Rc<T>>` fields)
28+
- **JSG resources**: `#[jsg_resource]` on struct + impl block; `#[jsg_method]` auto-converts `snake_case``camelCase`; methods with `&self`/`&mut self` become instance methods, methods without a receiver become static methods; `#[jsg_static_constant]` on `const` items exposes read-only numeric constants on both constructor and prototype (name kept as-is, no camelCase); resources integrate with GC via `Traced` + `GarbageCollected`: every named field is traced via `Traced::trace(&self.field, visitor)` and all non-traceable types use no-op `Traced` impls
2929
- **JSG properties**: two property macros on `#[jsg_resource]` impl blocks — `#[jsg_property(prototype|instance [, name = "..."] [, readonly])]` (registers an accessor; `prototype` maps to `JSG_PROTOTYPE_PROPERTY`, `instance` maps to `JSG_INSTANCE_PROPERTY`; `readonly` is a compile-time check preventing a paired setter; `name = "..."` overrides the JS name; prefer `prototype` in almost all cases), and `#[jsg_inspect_property]` (registered under a unique symbol, invisible to normal enumeration and string-key lookup, surfaced by `node:util` `inspect()`, equivalent to `JSG_INSPECT_PROPERTY`); setter auto-detected from `set_` prefix; read-only when no setter present; getter/setter `.length` and `.name` are set correctly when `spec_compliant_property_attributes` compat flag is enabled
30+
- **`Traced`**: core tracing trait in `jsg::wrappable`; built-ins include no-op impls for primitives/value types and delegating impls for wrappers/collections (`Option`, `Nullable`, `Vec`, maps/sets, `Cell`, `jsg::Rc`, `jsg::Weak`, `jsg::v8::Global`)
31+
- **`#[jsg_resource(custom_trace)]`**: suppresses the auto-generated `Traced` impl so the user can write their own; `GarbageCollected` (`memory_name`), `jsg::Type`, `jsg::ToJS`, and `jsg::FromJS` are still generated
3032
- **Formatting**: `rustfmt.toml``group_imports = "StdExternalCrate"`, `imports_granularity = "Item"` (one `use` per import)
3133
- **Linting**: `just clippy <crate>` — pedantic+nursery; `allow-unwrap-in-tests`
32-
- **Tests**: inline `#[cfg(test)]` modules; JSG tests use `jsg_test::Harness::run_in_context()`
34+
- **Tests**: inline `#[cfg(test)]` modules; JSG tests use `jsg_test::Harness::run_in_context()`. Always run the full `src/rust/...` test suite (`bazel test //src/rust/...`) rather than targeting a single crate — changes in shared crates like `jsg` or `jsg-macros` can break downstream consumers
3335
- **FFI pointers**: functions receiving raw pointers must be `unsafe fn` (see `jsg/README.md`)
3436
- **Parameter ordering**: `&Lock` / `&mut Lock` must always be the first parameter in any function that takes a lock (matching the C++ convention where `jsg::Lock&` is always first). This applies to free functions, trait methods, and associated functions (excluding `&self`/`&mut self` receivers which come before `lock`).
3537
- **Method naming**: do not use `get_` prefixes on methods — e.g. `buf.backing_store()` not `buf.get_backing_store()`. Static constructors belong on the marker struct (`impl ArrayBuffer { fn new(...) }`) not on `impl Local<'_, ArrayBuffer>`.

0 commit comments

Comments
 (0)