Deduplicate jsc bindings, runtime api, test matchers, and shell builtins

AL
2mo ago
+2708-3991

Dedup pass over the jsc bindings, runtime APIs, bun:test matchers and shell builtins. Only moves and removes code; no intended behavior change. 45 files, about -1.4k lines net.

  • ConsoleObject: TagPayload (a mirror of Tag that existed to carry one variant's payload) is gone; TagResult carries the payload as an Option. WrappedWriter::new replaces 32 struct literals and the per-function pf! macros.
  • VirtualMachine / jsc_hooks: one wait loop behind load_entry_point and load_entry_point_for_test_runner; auto_tick / auto_tick_active become auto_tick<const ACTIVE: bool>.
  • bun:test: toEqual / toStrictEqual share one implementation; the toHave*With family shares argument comparison, mock.results parsing and failure epilogues.
  • Shell: basename / dirname share PathBuiltin; cp/ls/mkdir/touch get their OutputTaskVTable impl from one macro; subproc stdio init is one match with per-arm cfg; the Blob stdin writer is shared with Bun.spawn.
  • dns: generic PendingCacheKey<Req> and one pending-chain drain replace the per-record-kind copies.
  • valkey: the seven command-shape macros collapse into cmd! and cmd_varargs!; all commands keep their name, wire command, argument names and state requirement.
  • Smaller: AsyncModule error builders, CryptoHasher / PasswordObject / csrf argument parsing, napi create_string_* prologue, bake init_transpiler_impl and the DevServer field-exhaustiveness macro, udp setsockopt helpers, ipc drain loops, socket callback tail, CodeCoverage line mapping.

One deliberate non-1:1 spot: mkdir's on_io_writer_chunk alone had an Idle | Done => panic! arm, which the macro drops to match the other three builtins; Idle still panics one frame down in Mkdir::next, and Done is only set after the last chunk callback has run.

Dropped along the way because main landed its own version: the cron consolidation (#40024) and the URL unification (#40238).

Verified with a debug build, cargo clippy --workspace, rust:check-all (12 targets), and the touched suites (console / inspect, expect + matchers, shell, crypto, csrf, dns, ipc, socket, udp, napi, bake, coverage, valkey), plus byte-for-byte differential checks against canary for the matcher messages, shell output and publish errors. The unified auto_tick is re-compared statement for statement against main's two functions after every rebase.

Review-driven commits
  • Second commit: mordant's same_match_twice on toHaveNthReturnedWith / toHaveLastReturnedWith, which now share mock::check_returned_with.
  • Third commit: parse_mock_result looks the result tag up through the existing RETURN_STATUS_MAP.
  • Fourth commit: call_socket_handler takes its optional extra argument as an Option<JSValue>.
Rebase notes

Rebased onto main nineteen times. Where main changed code this PR had moved into a helper, the change is applied once inside the helper; where main landed its own version of a piece (cron #40024, URL #40238, the websocket publish prologue #39389), the piece was dropped. The larger re-derivations:

  • #40238 and #40374 (String ownership and borrowed-bytes lifetimes): applied inside the ConsoleObject print_* functions, parse_verify_algorithm, CryptoHasher::hash, the napi string helpers, the shell argument helper, parse_mock_result, and the AsyncModule error builders (now fallible, same nine properties in the same order as main's inlined versions).
  • #40251 and #40410 (redundant exception checks, missing exception checks): applied inside parse_verify_algorithm, the ipc drain helpers (dispatch::fold around handle_ipc_message), and the matchers' get_direct_index calls.
  • #40478 (RefPtr releases on Drop): DNSLookup.resolver is a RefPtr as on main; the manual deref sites main deleted were never in this PR's helpers.
  • #40511 (Stdio::ArrayBuffer removed, PinnedArrayBuffer redirects): the shared stdin writer is Blob-only on both platform arms; the shell's single Readable::init takes main's redirect_buf and hands the BufferedOutput to PipeReader::create as main does.
  • #40516 (refcounted types own their teardown): the shell's unified Writable::init keeps main's FileSink::create_with_pipe value return on the Windows arm; the dns Resolver Drop / RefPtr::init_ref guards and the ProcessHandle holder land outside this PR's helpers.
  • #40678 (bun test --parallel coverage wire format): the resolve_line / map_to_original closures in CodeCoverage::generate sit on top of main's ByteRange loops (for block in blocks, set(stmts.len())).
  • #40068 (missing exception checks in the matchers): DiffFormatter::new(global, received, expected, not)? replaces the struct literal once inside mock::throw_diff and once in the unified toEqual, so the six toHave*With matchers and toStrictEqual get main's fallible formatting through the shared code. The matcher suites pass under BUN_JSC_validateExceptionChecks=1.
  • #39541 (no startup GC): the perform_gc() removal applied once in the shared entry-point wait; #39541's own tests pass here.
  • #29339's 45 new redis commands converted to the new macros; #38379 / #40091 (mkdir, touch, ls) and #39615 / #39856 / #40022 (socket) land beside the shared code, not inside it.
Pre-existing items surfaced by review, not changed here

Handed off: the redis bitcount range / hscan cursor argument handling.

Follow-ups: shell stdio init StdioResult::unwraps as error returns; redis keys argument reported as key rather than pattern; dns PendingCacheKey::append as an unsafe fn; bake production init_transpiler freeing the arena projection like DevServer does; sealing StaticHasher so the Digest cast is enforced by the trait; harmonizing the three napi string failure paths; the cron consolidation redone against #40024's shape.