Skip to content

Remove dead code from the JSC bindings, the socket and stream source classes, bun_http, and two orphaned data files - #42756

Open
robobun wants to merge 1 commit into
mainfrom
robobun/1ebc1493/dead-code-sweep
Open

robobun wants to merge 1 commit into
mainfrom
robobun/1ebc1493/dead-code-sweep

Conversation

@robobun

@robobun robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • src/jsc/bindings/webcore/JSDOMConvertResult.h (268 lines) defines ConversionResult<T>, and nothing uses it. src/jsc/bindings/dh-primes.h holds only four #include lines.
  • TCPSocket/TLSSocket endBuffered is installed only under the private name $end, and no built-in JS calls .$end(. The isClosed getter of the internal stream source handles has no reader, so NewSource::is_closed is write-only.
  • Two data files have no reader: bench/snippets/_fixture.txt (its reader went in Rewrite Bun.escapeHTML using Highway SIMD #31483) and the protocol.json snapshot in packages/bun-debug-adapter-protocol.

Fix

  • Delete each item: 29 files, 5892 lines removed. The two data files are 5389 of them, about 500 are code. The Notes list every symbol.
  • Correct because each removed name has zero references in src/, packages/, scripts/, test/ and build/debug/codegen/ outside its own definition. User code cannot spell the private names $end and $bunNativePtr.
  • Follow-through: write_or_end_buffered::<IS_END> had one instantiation left and becomes write_buffered_impl. ByteBlobLoader::parent() lost its only caller and goes too.
  • Verified: bun bd and bun run rust:check-all (12 targets) pass. Socket, node:net, streams, fetch, crypto and source-lint test files run with the debug build (list in the Notes).

Background

  • A *.classes.ts file declares the JS members of a native class. privateSymbol: "end" installs the member only under the private name $end, which only built-in JS in src/js can spell.
  • src/bun_core/feature_flags.rs holds compile-time bool constants. DISABLE_COMPRESSION_IN_HTTP_CLIENT is false, so the identity arm of ACCEPT_ENCODING_HEADER is never selected.
  • 34 other dead-code pull requests are open. This one deletes nothing that they delete. Eight files overlap at other lines, and their hunks still apply on top of this branch.
Notes

Removed, one line each:

  • src/jsc/bindings/webcore/JSDOMConvertResult.h (whole file): ConversionResult<T>, ConversionResultException, Detail::ConversionResultStorage. rg -w for the three names hits only the header. Its only includer was JSDOMConvertBase.h.
  • src/jsc/bindings/dh-primes.h (whole file): a license comment and four #include <openssl/...> lines since Remove dead code from uws_sys, webcore bindings, crash_handler, and scripts #37181 and Remove dead code from install, event_loop, dns, mysql protocol, C++ bindings, and JS internals #36970 removed the tables. ncrypto.cpp was the only includer. It now includes <openssl/mem.h> directly, and ncrypto.h already includes the other three headers.
  • TCPSocket/TLSSocket endBuffered ($end) in sockets.classes.ts, end_buffered in socket_body.rs, $end(): void in builtins.d.ts. No .$end( in src/js. endPrivateName appears only in the generated putDirect. Remove dead code from the node:http2 frame parser, the shell interpreter, NodeHTTPResponse, and three JS binding objects #42682 listed this member as found and not deleted, because of the IS_END follow-through that this change does.
  • Blob/File/BytesInternalReadableStreamSource.isClosed in streams.classes.ts, get_is_closed_from_js, the field NewSource::is_closed, its initializer, and its two stores (ByteBlobLoader::to_any_blob, ByteStream::to_any_blob). C++ reads the closed flag from the closer array (nativeCloserFlag, BunStreamSource.cpp), not from this getter. ByteBlobLoader::parent() had the store as its only caller. The Remove dead code from bun_bundler, bun_install, bun_runtime, bun_parsers, and the JSC private host functions #41088 hunks for both files still apply.
  • FeatureFlags::DISABLE_COMPRESSION_IN_HTTP_CLIENT (const false) and, in src/http/lib.rs, ACCEPT_ENCODING_NO_COMPRESSION, ACCEPT_ENCODING_HEADER_NO_COMPRESSION and the two intermediate constants. ACCEPT_ENCODING_HEADER keeps its value gzip, deflate, br, zstd.
  • ENABLE_SHADCN_UI (const true, under a stale "Disabled until Tailwind v4" comment) and its else arm in SourceFileProjectGenerator.rs.
  • The aliases starts_with_generic and ends_with_generic in src/bun_core/string/immutable.rs. has_prefix_t and has_suffix_t stay.
  • FALLBACK_HTML_VERSION in scripts/build/buildOptionsRs.ts. Nothing reads the generated constant.
  • AsyncContextFrame::create(JSGlobalObject*, JSValue, JSValue). The only call site uses the 4-argument overload.
  • The GlobalScope(VM&, Structure*) constructor in BunGlobalScope.h. Both subclasses pass a method table.
  • Zig::untagVoid(const char16_t*) in helpers.h. All four callers pass a const unsigned char*.
  • template<typename Visitor> static void visitChildren(JSCell*, Visitor&); in JSX509Certificate.h and JSNodePerformanceHooksHistogram.h. The prototype has no definition. DECLARE_VISIT_CHILDREN on the next line declares the real overloads.
  • #ifndef OPENSSL_NO_ENGINE / #include <openssl/engine.h> in NodeConstantsModule.h. BoringSSL's opensslconf.h defines OPENSSL_NO_ENGINE and <openssl/ssl.h> is included above, so the block is always skipped. The ENGINE_METHOD_* constants are numeric literals.
  • bun_BabyList_SynthProvider, bun_BabyList_SummaryProvider and their registration in misctools/lldb/bun_pretty_printer.py. The regex matches the Zig type name baby_list.BabyList(...). No BabyList type exists now (src/collections/vec_ext.rs:3).
  • bench/snippets/_fixture.txt (1610 lines, 131 KB of HTML). Its only reader, bench/snippets/escapeHTML.js, was deleted in Rewrite Bun.escapeHTML using Highway SIMD #31483. escapeHTML.mjs does not read it.
  • packages/bun-debug-adapter-protocol/src/protocol/protocol.json (3779 lines) and the line in scripts/generate-protocol.ts that wrote it. The script downloads the upstream DAP schema on every run and only the generated index.d.ts is imported. Last update: Update debugger things #8180. Remove dead code from the inspector protocol package, bun_jsc, bun_install, and the Windows named pipe modules #40557 deletes the equally unread v8 protocol.json of bun-inspector-protocol. The package's .gitattributes goes too: both patterns start with protocol/, the files are in src/protocol/, so they match nothing.

Tests run with the debug build: test/js/bun/net/socket.test.ts, test/js/node/net/node-net.test.ts, test/js/web/streams/streams.test.js (563 pass), test/js/web/fetch/body-stream.test.ts (9086 pass), test/js/web/fetch/blob.test.ts, test/js/bun/util/zstd.test.ts (checks the Accept-Encoding value), test/js/node/crypto/node-crypto.test.js, test/js/node/crypto/x509.test.ts, test/js/node/perf_hooks/perf_hooks.test.ts, test/js/node/async_hooks/AsyncLocalStorage.test.ts, test/js/bun/http/bun-server.test.ts, test/cli/create/create-jsx.test.ts, and test/internal/source-lints/{dead-code-escapes,host-export-callers,primordials-exports}.test.ts. The failures are the same with the unmodified release build in the same container: connects to 127.0.0.1 and IPv6 listens are refused there, create-jsx needs the network, and one AsyncLocalStorage test passes the 5 s limit under ASAN.

How the candidates were found:

  • Five search passes: built-in JS, the C++ bindings (two halves), scripts and packages, and Rust cfgs, features and orphan files. Then a per-class comparison of every *.classes.ts member against src/js.
  • cargo hawk check --only dead-public for linux x64, darwin arm64 and windows x64. 823 findings: 668 FFI layout fields, 90 entries of OS constant tables, 23 enum variants of external code tables. Of the 19 functions and methods, strings::split_once is unused but sits next to a hunk of Remove dead code from bun_jsc, bun_runtime, bun_core, and the JSC bindings #40824. The rest are debug-only or diagnostic, named by the class generator templates, or already deleted by an open pull request.
  • clang -fsyntax-only over the 165 Bun C++ translation units with -Wunused-function -Wunused-member-function -Wunused-template -Wunused-const-variable. 11 warnings: all are platform-gated, part of an intentional overload set, or already deleted by an open pull request.
  • A repo-wide identifier index (Rust definitions named nowhere else) and a file index (files whose name appears nowhere else).

Found, not deleted here:

  • NodeHTTPResponse ref, ended, onwritable: no reader in src/js, but test/js/node/http/node-http-uaf.test.ts sets handle.onwritable through Object.getOwnPropertySymbols, and node:http: finish a response when its body leaves, not when end() buffers it #41822 starts to use it.
  • The SQL connection handles: connected, unref, the MySQL ref (with the poll_ref_hostfns! macro), and the onconnect/onclose accessors have no JS reader. The queries getter initializes its array lazily, so the removal needs more care than a deletion.
  • The myersDiff raw-list export of internal/assert/myers_diff and Output::List in node_assert.rs: nothing consumes it, but assert: render Myers diffs natively #39924 kept it on purpose.
  • The DOMJIT path of src/codegen/generate-classes.ts (about 175 lines, plus the DOMJIT: entries in three *.classes.ts files): class-definitions.ts sets DOMJIT = undefined on every entry, so the generator never sees one. It looks like a deliberate kill switch. It needs a maintainer decision.
  • Bun__signpost_emit and generated_perf_trace_events.h: no caller on any platform, but src/bun_core/util.rs documents them as plumbing that is not wired yet.
  • The OpenSSL 3 only branches of ncrypto.cpp (about 75 lines): the file keeps the #if structure of nodejs/ncrypto on purpose.
  • JSMessagePort::visitAdditionalChildrenInGCThread: a no-op because MessagePort::locallyEntangledPort() always returns null. GC code in four files, two of them in Remove dead code from the WebCore bindings and 20 Rust crates #40525.
  • src/js_parser/benches/string_map_vs_hashmap.rs (800 lines) and its criterion/regex dev-dependencies: it compiles, and nothing runs it.
  • src/runtime/cli/init/react-*/bun.lock (430 lines): not embedded with include_bytes!, but kept in sync with the templates by hand.
  • The CI build modes cpp-only, rust-only, link-only, rust-and-link (about 525 lines in scripts/build): ci: build C++, Rust and link in one build-bun step instead of build-cpp + build-bun #37733 said they can go if the one-step layout holds up. Three tests name them.
  • ERR_REDIS_INVALID_DATABASE in ErrorCode.ts: no user, but Remove dead code from bun_css, bun_react_compiler, bun_jsc, and smaller crates #40690 and Remove dead code from bun_core, the JSC bindings, the debugger, and 24 other crates #41385 delete the lines next to it.
  • packages/bun-error/img/* (four images that nothing references) and their glob in scripts/glob-sources.ts: Remove dead code from the JSC bindings, headers.h, the console builtin, and bun_jsc #40232 edits the line next to the glob.
  • Hand-run tools that nothing references: scripts/lldb-inline-tool.cpp, scripts/lldb-inline.sh, scripts/github-metrics.ts, scripts/gamble.ts, scripts/trace.sh, scripts/debug-coredump.ts.
  • About 55 stale lines in .gitignore, .prettierignore, .dockerignore and .gitattributes for paths that no longer exist.
  • CastedThisErrorBehavior::ReturnEarly in JSDOMCastThisValue.h: no user, but the else arms in JSDOMAttribute.h that implement it stay reachable for the other values, so the enumerator stays with them.
  • bun_core::strings::split_once (the multi-byte delimiter form): no caller, but it sits next to the rsplit_once hunk of Remove dead code from bun_jsc, bun_runtime, bun_core, and the JSC bindings #40824.

Self-reviewed: two adversarial passes (Rust/JS and C++) over 18 items, each asked to find a missed reference, a platform-gated use, or a behavior change. No concern survived. Three candidates were dropped on the way: a JSAbortController declaration that the build showed to be live, NodeHTTPResponse.onwritable (see above), and ReturnEarly (see above).

…classes, bun_http, and two orphaned data files
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 6 days. After that, they cost $0.25 per reviewed file.

Or wait 2 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 12e1807a-859e-402e-a63b-66538981e504

📥 Commits

Reviewing files that changed from the base of the PR and between 90ed911 and 61f3f52.

📒 Files selected for processing (29)
  • bench/snippets/_fixture.txt
  • misctools/lldb/README.md
  • misctools/lldb/bun_pretty_printer.py
  • packages/bun-debug-adapter-protocol/.gitattributes
  • packages/bun-debug-adapter-protocol/scripts/generate-protocol.ts
  • packages/bun-debug-adapter-protocol/src/protocol/protocol.json
  • scripts/build/buildOptionsRs.ts
  • src/bun_core/feature_flags.rs
  • src/bun_core/string/immutable.rs
  • src/http/lib.rs
  • src/js/builtins.d.ts
  • src/jsc/bindings/AsyncContextFrame.cpp
  • src/jsc/bindings/AsyncContextFrame.h
  • src/jsc/bindings/BunGlobalScope.h
  • src/jsc/bindings/JSNodePerformanceHooksHistogram.h
  • src/jsc/bindings/JSX509Certificate.h
  • src/jsc/bindings/dh-primes.h
  • src/jsc/bindings/helpers.h
  • src/jsc/bindings/ncrypto.cpp
  • src/jsc/bindings/webcore/JSDOMConvertBase.h
  • src/jsc/bindings/webcore/JSDOMConvertResult.h
  • src/jsc/modules/NodeConstantsModule.h
  • src/runtime/api/streams.classes.ts
  • src/runtime/cli/create/SourceFileProjectGenerator.rs
  • src/runtime/socket/socket_body.rs
  • src/runtime/socket/sockets.classes.ts
  • src/runtime/webcore/ByteBlobLoader.rs
  • src/runtime/webcore/ByteStream.rs
  • src/runtime/webcore/ReadableStream.rs

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

Status: ready for review.

How each deletion was checked:

  • rg -w for every removed name across src/, packages/, scripts/, test/ and build/debug/codegen/: no reference outside the definition.
  • bun bd (debug + ASAN) passes. bun run rust:check-all passes on all 12 targets. cargo clippy on bun_runtime, bun_http and bun_core is clean.
  • Test files run with the debug build are listed in the Notes of the description. The only failures also fail with the unmodified release build in the same container (refused 127.0.0.1 and IPv6 connects, no network for create-jsx).
  • 34 other dead-code pull requests are open. This one deletes nothing that they delete. For the eight files that overlap, their hunks still apply on top of this branch.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. Because it's a broad sweep removing ~500 lines of code across 29 files in many subsystems (socket host fns, stream sources, JSC C++ bindings, crypto includes) and touches a CODEOWNER-gated .d.ts file, a human look would still be worthwhile.

What was reviewed:

  • Grepped src/, packages/, and bench/ for every removed identifier (ConversionResult, $end on Socket, isClosed on stream sources, endBuffered/end_buffered, FALLBACK_HTML_VERSION, starts_with_generic, DISABLE_COMPRESSION_IN_HTTP_CLIENT, _fixture.txt, protocol.json) — no remaining callers; the ConversionResult hit in JSX25519Params.cpp is just a local variable name, and builtins.d.ts:223's global $end is the unrelated JSC private name.
  • Checked END_AFTER_FLUSH still has other setters in write_or_end, so removing end_buffered's set point doesn't strand the flag.
  • Checked convert<> in JSDOMConvertBase.h returns Converter<T>::ReturnType (not ConversionResult<T>), so deleting the header and its transitional-shim conversion operators doesn't break any call site.
  • Checked ByteStream retains its own parent_const() accessor; only the now-callerless ByteBlobLoader::parent() was dropped.
Extended reasoning...

Overview

This PR is a dead-code sweep removing ~5,900 lines (of which ~5,400 are two orphaned data files: bench/snippets/_fixture.txt and packages/bun-debug-adapter-protocol/src/protocol/protocol.json) and ~500 lines of actual code across 29 files. On the Rust side it removes an always-false feature flag and collapses the resulting Accept-Encoding const, drops the is_closed field and getter from NewSource stream sources plus their two write sites, removes the endBuffered ($end) socket host fn and collapses write_or_end_buffered<IS_END> into a non-generic write_buffered_impl, and deletes several unused re-exports and a stale ENABLE_SHADCN_UI gate. On the C++ side it deletes JSDOMConvertResult.h, the four-include-only dh-primes.h (replaced by a direct <openssl/mem.h> include in ncrypto.cpp), an unused AsyncContextFrame::create overload, a 2-arg GlobalScope constructor, redundant visitChildren template declarations already covered by DECLARE_VISIT_CHILDREN, and an always-skipped <openssl/engine.h> include. Tooling-side deletions cover an LLDB pretty-printer for a Zig-era type and a build-options constant with no reader.

Security risks

The changes brush against crypto code (ncrypto.cpp, dh-primes.h, NodeConstantsModule.h) but only at the include level — no logic, no key handling, no validation is touched. The dh-primes.h header contained nothing but four #include <openssl/...> lines and a license block; three of those headers are already pulled in by ncrypto.h, and the fourth (<openssl/mem.h>) is now included directly. The OPENSSL_NO_ENGINE block removal is safe because BoringSSL always defines that macro. No auth, permission, or input-validation paths are affected. I see no security risk in this diff.

Level of scrutiny

Medium-high. REVIEW.md is explicit that dead-code deletion is required scope and encouraged, but also that "before deleting odd-looking code, git-blame why it was written — it is usually load-bearing" and that public items escape lints so callers must be grepped manually. I grepped every removed public identifier and confirmed zero remaining references. The one nuanced case — the socket write_or_end_buffered<IS_END> collapse — preserves behavior for the surviving write_buffered caller (it always instantiated <false>), and Flags::END_AFTER_FLUSH still has its other setters in write_or_end. The ConversionResult<T> deletion is safe because convert<> in JSDOMConvertBase.h returns Converter<T>::ReturnType directly, not the wrapper type; the only grep hit is a local variable name.

Other factors

The PR description reports bun bd and bun run rust:check-all (12 targets) pass along with a substantial suite of socket, stream, fetch, crypto, and source-lint tests. However, .github/CODEOWNERS assigns *.d.ts to a specific owner, and this PR modifies src/js/builtins.d.ts (a one-line removal). Combined with the breadth — 29 files across sockets, streams, JSC bindings, HTTP client, crypto includes, build scripts, LLDB tooling, and two packages — the guidelines' "do not approve when the change is large or touches CODEOWNER-gated paths" applies, so I'm deferring rather than approving despite finding no defects.

@robobun

robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

The review found no defect, so there is nothing to change.

Two notes for the human look it recommends:

  • src/js/builtins.d.ts: the edit is one line. It removes $end(): void from interface Socket, which pairs with the removed endBuffered member (privateSymbol: "end"). The global declare function $end() on line 223 stays, because macro(end) in BunBuiltinNames.h is still used by the streams code.
  • END_AFTER_FLUSH: confirmed as in the review. write_or_end::<true> (the public end path) still sets it. Only the set point inside the removed IS_END arm is gone, and that arm had no caller.

The claude-find-issues check failed in 37 s inside the action itself (result is_error:true, zero turns). It did not reach the diff.

@robobun

robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 4:29 PM PT - Sep 14th, 2026

✅ @robobun, your commit 61f3f5216014752c3bc290196cbd67d7c8e5c0c6 passed in Build #115668! 🎉


🧪   To try this PR locally:

bunx bun-pr 42756

That installs a local version of the PR into your bun-42756 executable, so you can run:

bun-42756 --bun

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants