Skip to content

Port SocketConfig to bindings generator#23755

Merged
Jarred-Sumner merged 31 commits into
mainfrom
taylor.fish/socket-config
Oct 19, 2025
Merged

Port SocketConfig to bindings generator#23755
Jarred-Sumner merged 31 commits into
mainfrom
taylor.fish/socket-config

Conversation

@taylordotfish

@taylordotfish taylordotfish commented Oct 16, 2025

Copy link
Copy Markdown
Contributor

(For internal tracking: fixes STAB-1471, STAB-1472, STAB-1473, STAB-1474, STAB-1475, STAB-1476, STAB-1480, STAB-1481)

(For internal tracking: fixes STAB-1471, STAB-1472, STAB-1473,
STAB-1474, STAB-1475, STAB-1476)
@robobun

robobun commented Oct 16, 2025

Copy link
Copy Markdown
Collaborator
Updated 5:55 PM PT - Oct 18th, 2025

@Jarred-Sumner, your commit 7b30f0e has some failures in Build #29600 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 23755

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

bun-23755 --bun

@coderabbitai

coderabbitai Bot commented Oct 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Refactors socket handler lifecycle to use local handler clones and explicit deinitialization, ports SocketConfig/Handlers to bindgenv2-generated payloads, extends bindgenv2 with "loose" nullable/primitive types, adjusts Zig/C++ binding utilities, and adds allocator-aware string/memory helpers.

Changes

Cohort / File(s) Summary
Socket core & handlers
src/bun.js/api/bun/socket.zig, src/bun.js/api/bun/socket/Handlers.zig
Replace cross-instance handler protect/unprotect with locally-scoped handler clones, add Handlers.fromGenerated/fromJS routing, Handlers.clone and Handlers.deinit, loop-driven callback wiring via callback_fields, and lifecycle/async-context propagation.
Listener, connect, and TLS flow
src/bun.js/api/bun/socket/Listener.zig, src/bun.js/api/server/SSLConfig.zig
Convert listen/reload/connect paths to ownership-aware hostname/port/ssl/handler handling, defer/deinit old handlers on replacement, add SSLConfig.fromGenerated plus takeProtos/takeServerName helpers.
Generated bindings (bindgenv2)
src/bun.js/api/bun/socket/SocketConfig.bindv2.ts
New bindgenv2 definitions: BinaryType enum, Handlers dictionary (SocketConfigHandlers), TLS union, and SocketConfig dictionary with mapped fields and generated conversion enabled.
Bindgen C++ union/variant init
src/bun.js/bindings/Bindgen/ExternTraits.h, src/bun.js/bindings/Bindgen/ExternUnion.h
Replace two-step initFromVariant with constructor-based initialization; add constructor overload for ExternUnion(std::variant&&) and adjust macros accordingly.
IDL converters & types (C++)
src/bun.js/bindings/BunIDLConvert.h, src/bun.js/bindings/BunIDLConvertNumbers.h, src/bun.js/bindings/BunIDLHumanReadable.h, src/bun.js/bindings/BunIDLTypes.h, src/bun.js/bindings/webcore/JSDOMConvertNullable.h
Add Converter specializations for IDLLooseNullable and IDLLooseInteger; map human-readable name for loose nullable; introduce IDLLooseNullable/IDLLooseInteger templates; remove some humanReadableName members; simplify nullable converter return path.
Bindgenv2 type-system: enums & optionals
src/codegen/bindgenv2/internal/enumeration.ts, src/codegen/bindgenv2/internal/optional.ts
Enumeration now accepts alias arrays and detects duplicates via valueMap; add LooseNullableType and looseNullable() with centralized bindgenOptional helper.
Bindgenv2 primitives & strings
src/codegen/bindgenv2/internal/primitives.ts, src/codegen/bindgenv2/internal/string.ts
Introduce IntegerType/LooseIntegerType hierarchy, LooseBool/LooseF64/FiniteF64 and loose integer exports; add LooseString and String.loose accessor.
Bindgenv2 tooling & config
src/codegen/bindgenv2/internal/base.ts, src/codegen/bindgenv2/internal/dictionary.ts, src/codegen/bindgenv2/tsconfig.json
Add JSDoc to optional/nullable getters, permit LooseNullableType as undefined in dictionary checks, add tsconfig path alias and include pattern for *.bindv2.ts.
Zig string & memory utilities
src/bun.js/bindings/ZigString.zig, src/memory.zig
Make ZigString.Slice.ptr default to explicit empty pointer; add ZigString.Slice.intoOwnedSlice(allocator) guaranteed-to-use-allocator; add memory.dropSentinel helper for sentinel-terminated pointers.
Tests & data
test/internal/ban-limits.json, test/js/bun/http/bun-listen-connect-args.test.ts, test/js/bun/net/socket.test.ts
Decrease a ban-limits count (255); remove Buffer-based hostname permutations for unix sockets; update expected invalid-port error message text.

Possibly related PRs

Suggested reviewers

  • pfgithub

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description does not follow the required template structure. The description template specifies two required sections: "### What does this PR do?" and "### How did you verify your code works?", but the provided description only contains a single line referencing internal issue tracking numbers without any of the required template sections. While the referenced issues provide context about objectives, the description itself is incomplete and lacks the structured information expected by the template.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Port SocketConfig to bindings generator" is clear, concise, and directly summarizes the primary change in the changeset. The title accurately reflects the main objective, which is evident from the extensive modifications to socket configuration files, the creation of new SocketConfig.bindv2.ts bindings, and updates to related handler and listener components. The title is specific enough to convey the primary change without being vague or generic.
Out of Scope Changes Check ✅ Passed All observable changes in the pull request are within scope of the stated objectives. The modifications span socket configuration files (socket.zig, Handlers.zig, Listener.zig, SocketConfig.bindv2.ts), supporting infrastructure for the bindings generator (enumeration, optional, primitives, string, dictionary types), IDL conversion and type definitions, and associated C++ and Zig utility code. These are all directly necessary to implement the SocketConfig bindings generator port and its supporting features as described in the linked issues. Minor test adjustments also align with the structural changes.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8ad7a and 7b30f0e.

📒 Files selected for processing (1)
  • src/bun.js/bindings/ZigString.zig (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/bindings/ZigString.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/bindings/ZigString.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/bindings/ZigString.zig
🧠 Learnings (2)
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In finalize() for objects holding JS references, release them using .deref() before destroy

Applied to files:

  • src/bun.js/bindings/ZigString.zig
📚 Learning: 2025-09-02T18:27:23.279Z
Learnt from: taylordotfish
PR: oven-sh/bun#22227
File: src/collections/multi_array_list.zig:24-24
Timestamp: 2025-09-02T18:27:23.279Z
Learning: The `#allocator` syntax in bun's custom Zig implementation is valid and does not require quoting with @"#allocator". Private fields using the `#` prefix work correctly throughout the codebase without special quoting syntax.

Applied to files:

  • src/bun.js/bindings/ZigString.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (3)
src/bun.js/bindings/ZigString.zig (3)

309-309: LGTM - Safe default initialization.

The default value &.{} provides a safe empty pointer, consistent with the zero-length default.


368-384: LGTM - Correct ownership transfer with proper defer semantics.

The implementation correctly handles ownership transfer:

  • Fast path: Returns the existing slice when the allocator is bun.default_allocator, avoiding allocation
  • Slow path: Allocates a new copy for other allocators (even if vtables match)

The defer semantics are correct per the author's earlier clarification - defer this.deinit() on line 382 is only registered when execution reaches that line, preventing any use-after-free in the fast path.

The conservative approach (only optimizing for default_allocator) ensures correctness and aligns with the objective to guarantee use of the provided allocator.

Based on learnings


386-386: LGTM - Accurate documentation.

The clarifying comment correctly distinguishes cloneIfNeeded from intoOwnedSlice - when the slice is already allocated, it returns the existing allocation (which may use a different allocator) rather than re-allocating with the provided allocator.


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

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4142f89 and 42323f8.

📒 Files selected for processing (11)
  • src/bun.js/api/bun/socket.zig (2 hunks)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/bun.js/api/bun/socket/Listener.zig (16 hunks)
  • src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1 hunks)
  • src/bun.js/api/server/SSLConfig.zig (3 hunks)
  • src/bun.js/bindings/Bindgen/ExternTraits.h (1 hunks)
  • src/bun.js/bindings/Bindgen/ExternUnion.h (1 hunks)
  • src/bun.js/bindings/ZigString.zig (2 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (4 hunks)
  • src/codegen/bindgenv2/tsconfig.json (1 hunks)
  • src/memory.zig (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/bun.js/api/bun/socket.zig
  • src/memory.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/bindings/ZigString.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket/Handlers.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/bindings/ZigString.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/bun.js/api/bun/socket.zig
  • src/memory.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/bindings/ZigString.zig
**/*.{cpp,h}

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible

Files:

  • src/bun.js/bindings/Bindgen/ExternUnion.h
  • src/bun.js/bindings/Bindgen/ExternTraits.h
🧠 Learnings (3)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
📚 Learning: 2025-09-02T18:27:23.279Z
Learnt from: taylordotfish
PR: oven-sh/bun#22227
File: src/collections/multi_array_list.zig:24-24
Timestamp: 2025-09-02T18:27:23.279Z
Learning: The `#allocator` syntax in bun's custom Zig implementation is valid and does not require quoting with @"#allocator". Private fields using the `#` prefix work correctly throughout the codebase without special quoting syntax.

Applied to files:

  • src/bun.js/bindings/ZigString.zig
📚 Learning: 2025-09-02T17:14:46.924Z
Learnt from: taylordotfish
PR: oven-sh/bun#22227
File: src/safety/alloc.zig:93-95
Timestamp: 2025-09-02T17:14:46.924Z
Learning: In bun's Zig codebase, they use a custom extension of Zig that supports private field syntax with the `#` prefix (e.g., `#allocator`, `#trace`). This is not standard Zig syntax but is valid in their custom implementation. Fields prefixed with `#` are private fields that cannot be accessed from outside the defining struct.

Applied to files:

  • src/bun.js/bindings/ZigString.zig
🧬 Code graph analysis (3)
src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1)
src/bun.js/api/server/SSLConfig.bindv2.ts (1)
  • SSLConfig (23-90)
src/bun.js/bindings/Bindgen/ExternTraits.h (1)
src/bun.js/bindings/BunIDLConvert.h (1)
  • std (255-279)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (121-123)
  • toQuotedLiteral (125-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (2)
src/bun.js/bindings/Bindgen/ExternUnion.h (2)

17-28: LGTM! Constructor-based initialization improves safety.

The new constructor properly enforces initialization at construction time, which is more robust than the previous two-step initialization pattern. The static_assert ensures FFI safety by verifying trivial copyability, and the variant visiting logic correctly activates the appropriate union member.


32-32: LGTM! Simplified field macro.

Moving the static_assert to the constructor (line 19) is more appropriate, as it only needs to be checked once per union specialization rather than per field.

Comment thread src/bun.js/api/bun/socket/Handlers.zig Outdated
Comment thread src/bun.js/api/bun/socket/Listener.zig Outdated
Comment thread src/bun.js/bindings/Bindgen/ExternTraits.h
Comment thread src/bun.js/bindings/ZigString.zig
Comment thread src/codegen/bindgenv2/internal/enumeration.ts Outdated
Comment thread src/memory.zig

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 42323f8 and 2436617.

📒 Files selected for processing (3)
  • src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1 hunks)
  • src/bun.js/bindings/Bindgen/ExternUnion.h (1 hunks)
  • test/internal/ban-limits.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/internal/ban-limits.json
**/*.{cpp,h}

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible

Files:

  • src/bun.js/bindings/Bindgen/ExternUnion.h
🧠 Learnings (1)
📚 Learning: 2025-09-26T01:10:04.233Z
Learnt from: nektro
PR: oven-sh/bun#22781
File: test/internal/ban-limits.json:25-26
Timestamp: 2025-09-26T01:10:04.233Z
Learning: The test/internal/ban-limits.json file contains accurate counts that are validated by tests - if the counts were incorrect, the associated tests would fail, so the values in this file reflect the actual current state of the codebase.

Applied to files:

  • test/internal/ban-limits.json
🧬 Code graph analysis (1)
src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1)
src/bun.js/api/server/SSLConfig.bindv2.ts (1)
  • SSLConfig (23-90)
🔇 Additional comments (1)
src/bun.js/bindings/Bindgen/ExternUnion.h (1)

33-33: LGTM! Field macro simplification is consistent with constructor-based validation.

Moving the static_assert to the constructor allows this macro to focus solely on field declarations, improving clarity.

Comment on lines +17 to +29
ExternUnion(std::variant<T0 __VA_OPT__(, ) __VA_ARGS__>&& variant) \
{ \
using This = std::decay_t<decltype(*this)>; \
static_assert(std::is_trivially_copyable_v<This>); \
const std::size_t index = variant.index(); \
std::visit([this, index](auto&& arg) { \
using Arg = std::decay_t<decltype(arg)>; \
BUN_BINDGEN_DETAIL_FOREACH( \
BUN_BINDGEN_DETAIL_EXTERN_UNION_VISIT, \
T0 __VA_OPT__(, ) __VA_ARGS__) \
}, \
std::move(variant)); \
} \

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.

🧹 Nitpick | 🔵 Trivial

LGTM! Constructor correctly initializes union from variant.

The new constructor properly achieves STAB-1475's goal: it allows ExternUnion to have a user-defined constructor while remaining trivially copyable (verified by the static_assert). The initialization logic using std::visit with index verification is sound.

Consider adding a defensive check after the lambda to catch unexpected index mismatches:

         std::visit([this, index](auto&& arg) {
             using Arg = std::decay_t<decltype(arg)>;
             BUN_BINDGEN_DETAIL_FOREACH(
                 BUN_BINDGEN_DETAIL_EXTERN_UNION_VISIT,
                 T0 __VA_OPT__(, ) __VA_ARGS__)
+            // If we reach here, no field was initialized
+            __builtin_unreachable();
         },
             std::move(variant));

This would make logic errors in the macro system fail fast rather than silently leaving the union uninitialized.

🤖 Prompt for AI Agents
In src/bun.js/bindings/Bindgen/ExternUnion.h around lines 17-29, add a defensive
runtime check immediately after the std::visit call to ensure the union was
actually initialized for the expected variant index; if the active member does
not match the captured index, fail fast (e.g., assert(false) or call
std::terminate()/abort with a clear message) so macro-system logic errors cannot
leave the union uninitialized.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/bun.js/api/bun/socket/Handlers.zig (1)

290-304: Restore hostname defaults and propagate flags outside the hostname branch.

This is the same regression called out earlier: rejecting configs that omit both hostname and unix breaks Bun.listen({ port: 0 })/Bun.connect({ port: 80 }), which rely on the implicit default host selection. We still need to materialize a default host (0.0.0.0 for servers, 127.0.0.1 for clients) whenever neither field is provided, while requiring an explicit port. Additionally, because the flag assignments now sit inside the hostname branch, any unix or fd-based config silently drops exclusive, allowHalfOpen, reusePort, and ipv6Only. Both behaviors worked before the refactor and must remain intact.

         } else if (generated.hostname.get()) |hostname| {
             bun.assertf(hostname.length() > 0, "truthy bindgen string shouldn't be empty", .{});
             result.hostname_or_unix = hostname.toUTF8(bun.default_allocator);
             const slice = result.hostname_or_unix.slice();
             result.port = generated.port orelse bun.URL.parse(slice).getPort() orelse {
                 return global.throwInvalidArguments("Missing \"port\"", .{});
             };
-            result.exclusive = generated.exclusive;
-            result.allowHalfOpen = generated.allow_half_open;
-            result.reusePort = generated.reuse_port;
-            result.ipv6Only = generated.ipv6_only;
         } else {
-            return global.throwInvalidArguments("Expected either \"hostname\" or \"unix\"", .{});
+            const port_value = generated.port orelse {
+                return global.throwInvalidArguments("Missing \"port\"", .{});
+            };
+            const default_host: []const u8 = if (is_server) "0.0.0.0" else "127.0.0.1";
+            const dup = bun.handleOom(bun.default_allocator.dupe(u8, default_host));
+            result.hostname_or_unix = .init(bun.default_allocator, dup);
+            result.port = port_value;
         }
+        result.exclusive = generated.exclusive;
+        result.allowHalfOpen = generated.allow_half_open;
+        result.reusePort = generated.reuse_port;
+        result.ipv6Only = generated.ipv6_only;
src/bun.js/api/bun/socket/Listener.zig (1)

645-647: Use takeServerName() when populating TLS SNI.

We’re still draining ALPN by calling takeProtos() for the server_name field, so SNI never propagates (and the ALPN list gets consumed twice). This is the same bug flagged earlier—please use takeServerName() for SNI and keep takeProtos() solely for the ALPN payload.

-                    .server_name = if (ssl) |s| s.takeProtos() else null,
+                    .server_name = if (ssl) |s| s.takeServerName() else null,-                .server_name = if (ssl) |s| s.takeProtos() else null,
+                .server_name = if (ssl) |s| s.takeServerName() else null,

Also applies to: 779-782

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 74a159c and 84ef46c.

📒 Files selected for processing (15)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/bun.js/api/bun/socket/Listener.zig (16 hunks)
  • src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1 hunks)
  • src/bun.js/bindings/BunIDLConvert.h (1 hunks)
  • src/bun.js/bindings/BunIDLConvertNumbers.h (1 hunks)
  • src/bun.js/bindings/BunIDLHumanReadable.h (1 hunks)
  • src/bun.js/bindings/BunIDLTypes.h (2 hunks)
  • src/bun.js/bindings/webcore/JSDOMConvertNullable.h (1 hunks)
  • src/codegen/bindgenv2/internal/base.ts (1 hunks)
  • src/codegen/bindgenv2/internal/dictionary.ts (1 hunks)
  • src/codegen/bindgenv2/internal/optional.ts (4 hunks)
  • src/codegen/bindgenv2/internal/primitives.ts (6 hunks)
  • test/js/bun/http/bun-listen-connect-args.test.ts (0 hunks)
  • test/js/bun/net/socket.test.ts (1 hunks)
  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs (2 hunks)
💤 Files with no reviewable changes (1)
  • test/js/bun/http/bun-listen-connect-args.test.ts
🧰 Additional context used
📓 Path-based instructions (16)
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/js/bun/net/socket.test.ts
  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs
test/js/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place JavaScript and TypeScript tests under test/js/

Files:

  • test/js/bun/net/socket.test.ts
test/js/bun/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)

Files:

  • test/js/bun/net/socket.test.ts
test/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable

Files:

  • test/js/bun/net/socket.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must live under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or roll your own random port
Prefer normalizeBunSnapshot for snapshotting test output instead of asserting raw strings
Do not write tests that assert absence of crashes (e.g., 'no panic' or 'no uncaught exception')
Use Bun’s Jest-compatible runner (import { test, expect } from "bun:test") for tests
Avoid shell commands like find or grep in tests; use Bun’s Glob and built-in tools instead
Prefer running tests via bun bd test and use provided harness utilities (bunEnv, bunExe, tempDir)
Use Bun.spawn with proper stdio handling and await proc.exited in process-spawning tests

Files:

  • test/js/bun/net/socket.test.ts
test/js/bun/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place Bun-specific API tests under test/js/bun/

Files:

  • test/js/bun/net/socket.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Use bun:test for files ending with *.test.{ts,js,jsx,tsx,mjs,cjs}
Prefer concurrent tests (test.concurrent/describe.concurrent) over sequential when feasible
Organize tests with describe blocks to group related tests
Use utilities like describe.each, toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach) and track resources for cleanup

Files:

  • test/js/bun/net/socket.test.ts
test/**/*.{ts,tsx,js,jsx,mjs,cjs}

📄 CodeRabbit inference engine (test/CLAUDE.md)

For large/repetitive strings, use Buffer.alloc(count, fill).toString() instead of "A".repeat(count)

Files:

  • test/js/bun/net/socket.test.ts
  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs
test/js/{bun,node}/**

📄 CodeRabbit inference engine (test/CLAUDE.md)

Organize unit tests by module under /test/js/bun/ and /test/js/node/

Files:

  • test/js/bun/net/socket.test.ts
  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs
**/*.{cpp,h}

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible

Files:

  • src/bun.js/bindings/BunIDLHumanReadable.h
  • src/bun.js/bindings/BunIDLConvertNumbers.h
  • src/bun.js/bindings/webcore/JSDOMConvertNullable.h
  • src/bun.js/bindings/BunIDLTypes.h
  • src/bun.js/bindings/BunIDLConvert.h
test/js/node/test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style

Files:

  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs
test/js/node/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place Node.js compatibility tests under test/js/node/

Files:

  • test/js/node/test/parallel/test-net-connect-custom-lookup-non-string-address.mjs
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
🧠 Learnings (7)
📚 Learning: 2025-10-12T02:22:34.373Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-10-12T02:22:34.373Z
Learning: Applies to test/{test/**/*.test.{ts,js,jsx,tsx,mjs,cjs},test/js/node/test/{parallel,sequential}/*.js} : Never hardcode port numbers; use `port: 0` to get a random port

Applied to files:

  • test/js/bun/net/socket.test.ts
📚 Learning: 2025-10-04T09:51:30.294Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.294Z
Learning: Applies to test/**/*.test.{ts,tsx} : In tests, always use port: 0; do not hardcode ports or roll your own random port

Applied to files:

  • test/js/bun/net/socket.test.ts
📚 Learning: 2025-10-01T21:49:27.862Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/BunIDLConvert.h:29-42
Timestamp: 2025-10-01T21:49:27.862Z
Learning: In Bun's IDL bindings (src/bun.js/bindings/BunIDLConvert.h), IDLStrictNull intentionally treats both undefined and null as null (using isUndefinedOrNull()), matching WebKit's IDLNull & IDLNullable behavior. This is the correct implementation and should not be changed to only accept null.

Applied to files:

  • src/bun.js/bindings/BunIDLHumanReadable.h
  • src/bun.js/bindings/webcore/JSDOMConvertNullable.h
  • src/bun.js/bindings/BunIDLTypes.h
  • src/bun.js/bindings/BunIDLConvert.h
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/bun.js/bindings/BunIDLConvertNumbers.h
  • src/bun.js/bindings/webcore/JSDOMConvertNullable.h
  • src/bun.js/bindings/BunIDLTypes.h
  • src/bun.js/bindings/BunIDLConvert.h
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/**/*.{h,cpp} : Use localToJSValue() when converting V8 handles to JSC values before performing JSC operations

Applied to files:

  • src/bun.js/bindings/webcore/JSDOMConvertNullable.h
📚 Learning: 2025-10-01T21:48:38.278Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/Bindgen/IDLTypes.h:1-3
Timestamp: 2025-10-01T21:48:38.278Z
Learning: In the Bun codebase, for `BunIDL*` and `Bindgen*` headers (e.g., BunIDLTypes.h, Bindgen/IDLTypes.h), it's acceptable to rely on transitive includes for standard library headers like <type_traits> and <utility> rather than including them explicitly.

Applied to files:

  • src/bun.js/bindings/BunIDLTypes.h
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/{js_bindings,BunObject}.classes.ts : Add JSDoc comments to JavaScript binding class definitions

Applied to files:

  • src/codegen/bindgenv2/internal/base.ts
🧬 Code graph analysis (7)
src/bun.js/bindings/BunIDLConvertNumbers.h (2)
src/bun.js/bindings/BunIDLTypes.h (8)
  • WebCore (19-29)
  • WebCore (32-32)
  • WebCore (35-35)
  • WebCore (43-43)
  • WebCore (44-44)
  • WebCore (45-45)
  • WebCore (46-46)
  • Bun (13-90)
src/bun.js/bindings/BunIDLConvertBase.h (2)
  • convert (43-47)
  • convert (55-66)
src/bun.js/bindings/BunIDLTypes.h (2)
src/bun.js/bindings/webcore/JSDOMConvertNullable.h (1)
  • WebCore (35-174)
src/bun.js/bindings/IDLTypes.h (11)
  • WebCore (51-256)
  • IDLType (94-95)
  • IDLType (98-99)
  • IDLType (102-114)
  • IDLType (116-117)
  • IDLType (119-120)
  • IDLType (203-209)
  • IDLType (347-352)
  • IDLType (354-362)
  • IDLType (364-365)
  • IDLType (379-380)
src/codegen/bindgenv2/internal/primitives.ts (2)
src/codegen/bindgen-lib.ts (10)
  • Type (18-32)
  • u8 (156-156)
  • u16 (157-157)
  • u32 (158-158)
  • u64 (159-159)
  • i8 (160-160)
  • i16 (161-161)
  • i32 (162-162)
  • i64 (163-163)
  • f64 (154-154)
src/codegen/bindgenv2/internal/base.ts (1)
  • CodeStyle (5-5)
src/bun.js/bindings/BunIDLConvert.h (2)
src/bun.js/bindings/BunIDLTypes.h (10)
  • WebCore (19-29)
  • WebCore (32-32)
  • WebCore (35-35)
  • WebCore (43-43)
  • WebCore (44-44)
  • WebCore (45-45)
  • WebCore (46-46)
  • Bun (13-90)
  • nullValue (26-26)
  • nullValue (77-77)
src/bun.js/bindings/webcore/JSDOMConvertNullable.h (7)
  • WebCore (35-174)
  • optional (77-85)
  • convert (88-93)
  • convert (95-100)
  • convert (101-106)
  • convert (107-112)
  • convert (129-134)
src/codegen/bindgenv2/internal/optional.ts (2)
src/codegen/bindgen-lib.ts (1)
  • Type (18-32)
src/codegen/bindgenv2/internal/base.ts (2)
  • nullable (14-16)
  • CodeStyle (5-5)
src/codegen/bindgenv2/internal/dictionary.ts (4)
src/codegen/bindgenv2/internal/optional.ts (1)
  • optional (11-32)
src/bun.js/bindings/BunIDLConvert.h (3)
  • optional (51-60)
  • optional (131-140)
  • optional (240-306)
src/bun.js/bindings/webcore/JSDOMConvertNullable.h (1)
  • optional (77-85)
src/bun.js/bindings/webcore/JSDOMConvertOptional.h (1)
  • optional (41-53)
src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1)
src/bun.js/api/server/SSLConfig.bindv2.ts (1)
  • SSLConfig (23-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (1)
src/codegen/bindgenv2/internal/primitives.ts (1)

199-229: Rename from finiteF64 to FiniteF64 is clean and complete.

No references to the old finiteF64 identifier exist anywhere in the codebase. The new FiniteF64 is used internally within primitives.ts and is not exported publicly via lib.ts. No downstream breakage.

Comment thread src/bun.js/bindings/BunIDLConvert.h
Comment thread src/codegen/bindgenv2/internal/optional.ts
@linear

linear Bot commented Oct 17, 2025

Copy link
Copy Markdown

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/codegen/bindgenv2/internal/enumeration.ts (1)

86-97: Fix array-to-string coercion in human-readable name generation.

Line 86 attempts to quote all values with values.map(v => `"${v}"`) but v can now be an array (for aliases). When an array is coerced to a string via template literal, it joins with commas, producing incorrect output like "bar,baz" instead of listing each alias separately.

Apply this diff to flatten all aliases before quoting:

-      const quotedValues = values.map(v => `"${v}"`);
+      const quotedValues = values
+        .flatMap(v => (typeof v === "object" ? v : [v]))
+        .map(s => `"${s}"`);
src/bun.js/api/bun/socket/Listener.zig (1)

494-499: Wrong allocator used to destroy Listener; can corrupt heap

Listener instances are created with handlers.vm.allocator.create(...), but deinit() frees with bun.default_allocator.destroy(this). Freeing with a different allocator is undefined behavior.

As per coding guidelines, use bun.destroy(this) (or the same allocator that created it):

     if (this.protos) |protos| {
         this.protos = null;
         bun.default_allocator.free(protos);
     }
-    bun.default_allocator.destroy(this);
+    bun.destroy(this);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 84ef46c and aaf1160.

📒 Files selected for processing (4)
  • src/bun.js/api/bun/socket/Listener.zig (16 hunks)
  • src/bun.js/bindings/Bindgen/ExternTraits.h (1 hunks)
  • src/bun.js/bindings/BunIDLConvert.h (1 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{cpp,h}

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible

Files:

  • src/bun.js/bindings/Bindgen/ExternTraits.h
  • src/bun.js/bindings/BunIDLConvert.h
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Listener.zig
🧠 Learnings (2)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
  • src/bun.js/bindings/BunIDLConvert.h
📚 Learning: 2025-10-01T21:49:27.862Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/BunIDLConvert.h:29-42
Timestamp: 2025-10-01T21:49:27.862Z
Learning: In Bun's IDL bindings (src/bun.js/bindings/BunIDLConvert.h), IDLStrictNull intentionally treats both undefined and null as null (using isUndefinedOrNull()), matching WebKit's IDLNull & IDLNullable behavior. This is the correct implementation and should not be changed to only accept null.

Applied to files:

  • src/bun.js/bindings/BunIDLConvert.h
🧬 Code graph analysis (2)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
src/bun.js/bindings/BunIDLConvert.h (3)
src/bun.js/bindings/BunIDLTypes.h (10)
  • WebCore (19-29)
  • WebCore (32-32)
  • WebCore (35-35)
  • WebCore (43-43)
  • WebCore (44-44)
  • WebCore (45-45)
  • WebCore (46-46)
  • Bun (13-90)
  • nullValue (26-26)
  • nullValue (77-77)
src/bun.js/bindings/webcore/JSDOMConvertNullable.h (7)
  • WebCore (35-174)
  • optional (77-85)
  • convert (88-93)
  • convert (95-100)
  • convert (101-106)
  • convert (107-112)
  • convert (129-134)
src/bun.js/bindings/BunIDLConvertBase.h (3)
  • optional (68-74)
  • convert (43-47)
  • convert (55-66)
🔇 Additional comments (10)
src/bun.js/bindings/Bindgen/ExternTraits.h (1)

71-75: Successfully addresses previous review feedback.

The initializer list now correctly matches the declaration order (data, then tag), which eliminates potential compiler warnings. While calling variant.index() after the move is valid (it accesses only the discriminator, not the contained value), capturing the index before moving would make the code slightly more explicit:

 explicit ExternVariant(std::variant<Args...>&& variant)
-    : data(std::move(variant))
-    , tag(static_cast<std::uint8_t>(variant.index()))
+    : tag(static_cast<std::uint8_t>(variant.index()))
+    , data(std::move(variant))
 {
 }

This preserves the initialization order fix while making the dependency on variant clearer. However, the current implementation is correct and the improvement is purely for readability.

Likely an incorrect or invalid review comment.

src/codegen/bindgenv2/internal/enumeration.ts (5)

14-23: LGTM: Alias support and unique-value extraction.

The documentation clearly explains the aliasing feature, and the uniqueValues extraction correctly handles both string and array inputs. The validation for empty arrays is appropriate.


31-39: Duplicate detection correctly implemented.

The valueMap construction with inline duplicate detection (via size comparison) correctly addresses the past review concern about duplicate aliases silently overwriting earlier entries. The logic ensures that repeated aliases across any variants are caught immediately.


74-79: LGTM: Index lookup via valueMap.

The change from positional lookup (values.indexOf(value)) to map-based lookup (valueMap.get(value)) correctly supports aliases and provides O(1) performance.


150-160: LGTM: C++ source emission updated for aliases.

The C++ mappings now correctly use valueMap.size and Array.from(valueMap.entries()) to emit all alias-to-enum-member pairs, ensuring each alias is mapped to the correct underlying integral value.


190-190: LGTM: Zig enum literals use unique values.

Correctly emits only the primary (unique) values in the Zig enum definition, avoiding duplicate enum members while still supporting alias-aware lookups via the valueMap in other language targets.

src/bun.js/api/bun/socket/Listener.zig (4)

306-310: Good: safe conversion of sentinel string for SNI check

Using std.mem.span for length check avoids UB and matches the sentinel-to-slice objective (STAB-1473). LGTM.


636-647: Good: server_name now uses takeServerName(), not takeProtos()

Correct separation of ALPN (.protos) and SNI (.server_name) when reusing or creating TLSSocket. This fixes the prior SNI misassignment.


770-782: Good: consistent takeServerName()/takeProtos() for new/previous sockets

Consistent, ownership-aware moves for ALPN and SNI across TCP/TLS connect paths. LGTM.


112-118: Good: deinitExcludingHandlers() + errdefer unprotect pattern

Deferring deinit while keeping handlers protected, and errdefer unprotect on failure, prevents handler leaks and double protects. LGTM.

Also applies to: 553-566

Comment thread src/bun.js/api/bun/socket/Listener.zig Outdated
Comment thread src/bun.js/api/bun/socket/Listener.zig
Comment thread src/bun.js/api/bun/socket/Listener.zig Outdated
Comment thread src/bun.js/api/bun/socket/Listener.zig
Comment thread src/bun.js/bindings/BunIDLConvert.h
Comment thread src/bun.js/bindings/BunIDLConvert.h
Comment thread src/codegen/bindgenv2/internal/enumeration.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)

82-82: Fix incorrect human-readable name generation.

Line 82 maps over the original values array, which may contain nested arrays for aliases. When stringified, these arrays produce malformed output like "foo,bar" instead of "foo". This results in confusing error messages.

Apply this diff to use uniqueValues instead:

-      const quotedValues = values.map(v => `"${v}"`);
+      const quotedValues = uniqueValues.map(v => `"${v}"`);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aaf1160 and 4bc6f44.

📒 Files selected for processing (2)
  • src/bun.js/api/bun/socket/Listener.zig (16 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Listener.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Listener.zig
🧠 Learnings (1)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (10)
src/codegen/bindgenv2/internal/enumeration.ts (6)

14-18: LGTM! Clear documentation and appropriate signature.

The JSDoc clearly explains the alias mapping behavior, and the function signature correctly reflects the new capability to accept arrays of strings for enum aliases.


31-39: LGTM! Duplicate detection correctly implemented.

The logic properly catches duplicate aliases by comparing the map size before and after insertion. If the size remains unchanged after set(), it indicates the key already existed, triggering an appropriate error. This correctly addresses the requirement to reject duplicate enum aliases.


41-55: LGTM! C++ member generation correctly refactored.

The loop properly iterates over uniqueValues for generating C++ member names and only handles naming collisions (not value duplicates, which are already validated earlier). The redundant duplicate check mentioned in past reviews has been correctly removed.


70-76: LGTM! Alias resolution correctly implemented.

The toCpp method properly uses valueMap.get(value) to resolve both primary values and aliases to their corresponding enum indices, with appropriate error handling for invalid values.


146-158: LGTM! C++ alias mappings correctly generated.

The C++ source generation properly uses valueMap.size and valueMap.entries() to emit all alias-to-enum mappings, ensuring each alias string correctly resolves to its corresponding enum member at runtime.


184-187: LGTM! Zig enum generation correctly uses primary values.

The Zig source generation appropriately uses uniqueValues to emit only the primary enum member names, as Zig enums don't require separate alias mappings.

src/bun.js/api/bun/socket/Listener.zig (4)

635-636: LGTM: Correct use of takeProtos() and takeServerName().

All connection paths now correctly use:

  • takeProtos() exclusively for the protos field (ALPN list)
  • takeServerName() exclusively for the server_name field (SNI)

This properly addresses the issue flagged in previous reviews where server_name was incorrectly assigned from takeProtos(), which would drain the ALPN list and prevent SNI propagation.

Also applies to: 645-646, 770-771, 780-781


112-112: LGTM: Proper use of deinitExcludingHandlers().

Both listen() and connectInner() correctly use deinitExcludingHandlers() to clean up the socket_config while preserving the handlers that are transferred to the created Listener or socket object. The errdefer at lines 117 and 559 ensures handlers are unprotected if an error occurs.

Also applies to: 554-554


130-130: LGTM: Correct ownership transfer with intoOwnedSlice().

All three sites properly use intoOwnedSlice() to transfer ownership of the hostname/path from socket_config to the created connection structure. The owned slices are properly cleaned up via connection.deinit() in error paths (see errdefer at line 584) and in the normal Listener.deinit().

Also applies to: 238-238, 576-576


304-311: LGTM: Safe server_name access after takeProtos().

Accessing ssl_config.server_name after calling takeProtos() at line 297 is safe because takeProtos() only transfers ownership of the protos field, leaving server_name intact in the SSLConfig. The null-terminated string is properly handled with std.mem.span() and length check.

Comment thread src/bun.js/api/bun/socket/Listener.zig
Comment thread src/codegen/bindgenv2/internal/enumeration.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)

82-93: Fix incorrect handling of array values in human-readable name generation.

When values contains arrays (e.g., ["foo", "bar"]), the template literal "${v}" will stringify the array as "foo,bar", which is incorrect. The human-readable name should only include the canonical (first) value of each enum variant, not all aliases.

Apply this diff to use uniqueValues instead:

-      const quotedValues = values.map(v => `"${v}"`);
+      const quotedValues = uniqueValues.map(v => `"${v}"`);
♻️ Duplicate comments (3)
src/codegen/bindgenv2/internal/enumeration.ts (1)

34-39: Consider a clearer duplicate detection pattern.

The current pattern checks if the map size changed after setting a value, which works but relies on side effects within the condition. A more idiomatic approach would separate the check from the modification.

Apply this diff for clearer intent:

   const valueMap = new Map<string, number>();
   for (const [value, index] of indexedValues) {
-    if (valueMap.size === valueMap.set(value, index).size) {
+    if (valueMap.has(value)) {
       throw RangeError(`duplicate enum value: ${util.inspect(value)}`);
     }
+    valueMap.set(value, index);
   }
src/bun.js/api/bun/socket/Listener.zig (1)

604-606: Do not panic on FD conversion; return a JS SystemError.

Panic kills the process. Convert to JS error (ENOMEM) instead.

Apply this diff:

-                        connection.fd = bun.FD.fromNative(osfd).makeLibUVOwned() catch
-                            @panic("failed to allocate file descriptor");
+                        connection.fd = bun.FD.fromNative(osfd).makeLibUVOwned() catch {
+                            const err = jsc.SystemError{
+                                .message = bun.String.static("failed to allocate file descriptor"),
+                                .syscall = bun.String.static("connect"),
+                                .code = bun.String.static("ENOMEM"),
+                            };
+                            return globalObject.throwValue(err.toErrorInstance(globalObject));
+                        };
src/bun.js/api/bun/socket/Handlers.zig (1)

315-321: Socket flags ignored for unix/fd; apply for all address types.

exclusive/allowHalfOpen/reusePort/ipv6Only are only set in the hostname branch. They should always reflect user options.

Apply these diffs:

Remove from hostname branch:

-            result.exclusive = generated.exclusive;
-            result.allowHalfOpen = generated.allow_half_open;
-            result.reusePort = generated.reuse_port;
-            result.ipv6Only = generated.ipv6_only;

Add once after the address selection, before return:

+        // Apply flags regardless of address type
+        result.exclusive = generated.exclusive;
+        result.allowHalfOpen = generated.allow_half_open;
+        result.reusePort = generated.reuse_port;
+        result.ipv6Only = generated.ipv6_only;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4bc6f44 and b5b016a.

📒 Files selected for processing (4)
  • src/bun.js/api/bun/socket.zig (8 hunks)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/bun.js/api/bun/socket/Listener.zig (17 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket/Listener.zig
  • src/bun.js/api/bun/socket.zig
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (8)
src/codegen/bindgenv2/internal/enumeration.ts (5)

14-23: LGTM!

The documentation clearly explains the array-to-same-value mapping, and the uniqueValues extraction logic correctly handles both string and array inputs. The use of Array.isArray() is idiomatic and clear.


41-55: LGTM!

The C++ member name generation logic correctly handles uniqueness and collisions. The redundant duplicate check has been appropriately removed as per previous review feedback.


71-76: LGTM!

The toCpp method correctly uses valueMap to resolve aliases to their corresponding enum members, with appropriate error handling for invalid values.


146-157: LGTM!

The C++ source generation correctly uses valueMap to include all aliases in the string-to-enum mapping, ensuring that all valid string values (including alternate spellings) are recognized.


186-187: LGTM!

The Zig source generation correctly uses uniqueValues to create one enum member per variant with its canonical name, which is the appropriate representation for Zig's type system.

src/bun.js/api/bun/socket.zig (1)

1352-1356: Good: proper handlers reload without leaks.

Deinit old, assign new by value. Matches new Handlers.deinit() semantics.

src/bun.js/api/bun/socket/Listener.zig (2)

94-96: Good: handlers reload is now safe.

Deinit then assign by value; no leak.


616-618: Review comment is incorrect: Listener.zig creation sites already ensure allocator consistency.

The flagged Listener.zig lines (616-618, 743-745, 761-764) all properly use handlers.vm.allocator for both creation and destruction:

  • Line 616: handlers.vm.allocator.create(Handlers) → freed at line 627: handlers.vm.allocator.destroy(prev_handlers)
  • Line 743: handlers.vm.allocator.create(Handlers) → freed at line 763: handlers.vm.allocator.destroy(prev_handlers)
  • Line 761-764: cleanup code showing destroy uses handlers.vm.allocator

Similarly, socket.zig line 1434 and its error path cleanup at line 1487 are in the same function (upgradeTLS) and both use bun.default_allocator consistently.

Comment thread src/bun.js/api/bun/socket/Handlers.zig
Comment thread src/codegen/bindgenv2/internal/enumeration.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/codegen/bindgenv2/internal/enumeration.ts (3)

38-52: Remove Set.add side‑effects in conditionals for C++ member naming.

The current pattern mutates the set during condition evaluation. A direct has() check is clearer and avoids subtle insertion‑order surprises.

Use:

   const cppMemberSet = new Set<string>();
   for (const value of uniqueValues) {
     let cppName = "k";
     cppName += value
       .split(/[^A-Za-z0-9]+/)
       .filter(x => x)
       .map(s => s[0].toUpperCase() + s.slice(1))
       .join("");
-    if (cppMemberSet.size === cppMemberSet.add(cppName).size) {
-      let i = 2;
-      while (cppMemberSet.size === cppMemberSet.add(cppName + i).size) {
-        ++i;
-      }
-    }
+    if (!cppMemberSet.has(cppName)) {
+      cppMemberSet.add(cppName);
+    } else {
+      let i = 2;
+      while (cppMemberSet.has(cppName + i)) ++i;
+      cppMemberSet.add(cppName + i);
+    }
   }

163-171: Honor strict string-only semantics for enum conversion; don’t coerce via ToString.

This specialization coerces non-strings with toWTFString(&globalObject). bindgenv2’s enumeration conversions intentionally require value.isString() without ToString coercion. Gate on isString() first.

Apply:

 template<> std::optional<${qualifiedName}>
 WebCore::parseEnumeration<${qualifiedName}>(
   JSC::JSGlobalObject& globalObject,
   JSC::JSValue value)
 {
-  return parseEnumerationFromString<::${qualifiedName}>(
-    value.toWTFString(&globalObject)
-  );
+  if (!value.isString()) {
+    return std::nullopt;
+  }
+  return parseEnumerationFromString<::${qualifiedName}>(
+    value.toWTFString(&globalObject)
+  );
 }

Based on learnings.


146-154: Be explicit about sort order of C++ mappings.

Default .sort() stringifies [key, index] tuples; it works but is implicit. Sorting by key avoids engine-dependent quirks and documents intent.

Apply:

-              Array.from(valueMap.entries())
-                .sort()
+              Array.from(valueMap.entries())
+                .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
                 .map(([value, i]) => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d7b5a6c and fccfb90.

📒 Files selected for processing (4)
  • src/bun.js/api/bun/socket.zig (9 hunks)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/bun.js/api/bun/socket/Listener.zig (18 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
  • src/bun.js/api/bun/socket.zig
  • src/bun.js/api/bun/socket/Listener.zig
🧠 Learnings (2)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern

Applied to files:

  • src/bun.js/api/bun/socket/Listener.zig
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (17)
src/bun.js/api/bun/socket/Handlers.zig (6)

21-33: LGTM: Debug tracking and field iteration helper.

The conditional protection_count provides runtime validation in CI, and callback_fields enables DRY iteration over callback fields.


92-94: Correct allocator usage.

Now properly uses vm.allocator.destroy() to match the allocator used for creation. This addresses the previously flagged allocator mismatch.


166-170: LGTM: Proper resource cleanup.

Correctly unprotects JSValue callbacks, deinitializes the promise, and defensively sets fields to undefined.


172-214: LGTM: Symmetric protection management.

The protect/unprotect/withAsyncContextIfNeeded methods correctly iterate over all callback fields with proper CI assertions.


216-228: LGTM: Clone properly protects new instance.

Creates an independent copy and correctly protects the cloned handlers.


243-258: LGTM: Flexible cleanup with ownership transfer.

The two deinit methods allow callers to selectively clean up handlers, supporting ownership transfer patterns.

src/bun.js/api/bun/socket.zig (4)

577-578: LGTM: Proper callback lifecycle for client renegotiation.

Correctly unprotects and clears onOpen after first handshake on client sockets to prevent duplicate calls on renegotiation.


1352-1356: LGTM: Proper handler reload lifecycle.

Now correctly deinitializes old handlers before assigning new ones, addressing the previous review concern about memory leaks during reload.


1397-1514: LGTM: TLS upgrade handler lifecycle.

Properly manages handler allocation with vm.allocator, deinitializes on error paths, and uses clone() to create independent protected instances for the raw socket.


1911-1933: LGTM: Duplex TLS upgrade handler allocation.

Consistently uses vm.allocator for handler allocation, matching the pattern used in other TLS upgrade paths.

src/bun.js/api/bun/socket/Listener.zig (7)

94-96: LGTM: Reload properly deinitializes handlers.

Correctly deinitializes old handlers before assigning new ones, addressing the previous review concern.


110-115: LGTM: Proper error cleanup with ownership separation.

Uses deinitExcludingHandlers() for the socket config and separate errdefer for handlers, enabling clean ownership transfer to the Listener.


123-172: LGTM: Windows named pipe ownership transfer.

Properly transfers ownership of hostname, handlers, and SSL protos to the Listener, with error cleanup via errdefer.


236-242: LGTM: Hostname ownership transfer.

Properly transfers hostname ownership to the connection union via intoOwnedSlice().


476-497: LGTM: Proper Listener cleanup with correct allocator.

Stores vm before deinitializing handlers to ensure vm.allocator.destroy() uses the correct allocator.


552-583: LGTM: Connect ownership and error handling.

Properly manages ownership of hostname and handlers with appropriate errdefer cleanup paths.


617-629: LGTM: Consistent previous handler cleanup.

All reconnection paths now properly deinitialize previous handlers before destroying them, using the correct vm.allocator. This addresses the previous review concerns.

Also applies to: 672-675, 762-765

Comment thread src/bun.js/api/bun/socket/Handlers.zig Outdated
Comment thread src/bun.js/api/bun/socket/Handlers.zig
Comment thread src/codegen/bindgenv2/internal/enumeration.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/bun.js/api/bun/socket/Handlers.zig (1)

277-323: Extract try expressions before struct initialization to enable proper errdefer cleanup.

The code at lines 283–293 violates Zig error handling best practices. Doing try directly inside struct field initializers creates no named binding for errdefer to attach to. If either try fails, the struct assignment may not complete, leaving fields uninitialized or resources uncleaned.

The issue:

  • try .fromGenerated(...) on line 289 (ssl field) and line 291 (handlers field) have no named bindings
  • errdefer result.deinit() on line 294 cannot clean up these values if their try expressions fail
  • This can lead to resource leaks or double-free when errors occur

Extract the try calls into locals with their own errdefer statements before constructing the struct, following Zig's recommended pattern:

+        const ssl_value = switch (generated.tls) {
+            .none => null,
+            .boolean => |b| if (b) .zero else null,
+            .object => |*ssl| try .fromGenerated(vm, global, ssl),
+        };
+        errdefer if (ssl_value) |*s| s.deinit();
+        
+        const handlers_value = try .fromGenerated(global, &generated.handlers, is_server);
+        errdefer handlers_value.deinit();
+        
         var result: SocketConfig = .{
             .hostname_or_unix = .empty,
             .fd = if (generated.fd) |fd| .fromUV(fd) else null,
-            .ssl = switch (generated.tls) {
-                .none => null,
-                .boolean => |b| if (b) .zero else null,
-                .object => |*ssl| try .fromGenerated(vm, global, ssl),
-            },
-            .handlers = try .fromGenerated(global, &generated.handlers, is_server),
+            .ssl = ssl_value,
+            .handlers = handlers_value,
             .default_data = if (generated.data.isUndefined()) .zero else generated.data,
         };
         errdefer result.deinit();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fccfb90 and 909bb68.

📒 Files selected for processing (1)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
🔇 Additional comments (7)
src/bun.js/api/bun/socket/Handlers.zig (7)

1-33: Good centralization of callback field names.

The callback_fields tuple (lines 23–33) enables DRY iteration and reduces duplication across protect/unprotect/withAsyncContextIfNeeded. The conditional protection_count for ci_assert-only debugging is clean.


92-94: Allocator mismatch fix is correct.

Using vm.allocator for destruction ensures handlers are destroyed with the same allocator that created them, preventing UB.


165-189: Lifecycle management looks sound.

deinit() properly unprotects all callbacks and clears promise. The unprotect() method correctly mirrors protect() and uses comptime Environment.ci_assert to conditionally guard the debug assertion.


191-198: Good use of callback_fields for consistency.

The withAsyncContextIfNeeded method correctly applies async context to each callback using the centralized callback_fields tuple, maintaining consistency with protection/unprotection logic.


215-227: Clone method properly re-protects callbacks.

The shallow copy preserves handler state and re-protects the cloned callbacks, ensuring proper lifecycle management.


243-257: Clever deinit pattern with preserved handler pointer.

deinitExcludingHandlers sets the struct to undefined while preserving the handlers pointer to avoid dangling references. This enables separate handler lifecycle management while maintaining valid pointers for cleanup code.


325-334: SocketConfig.fromJS correctly delegates.

The fromJS method properly instantiates the generated schema, defers cleanup, and delegates to fromGenerated, maintaining the two-tier construction pattern.

Comment thread src/bun.js/api/bun/socket/Handlers.zig

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/bun.js/api/bun/socket/Handlers.zig (1)

277-327: Socket configuration flags not applied to fd/unix paths.

Lines 319-322 set exclusive, allowHalfOpen, reusePort, and ipv6Only only when hostname is provided. When fd (line 300) or unix (lines 300-311) paths are taken, these flags remain at their default values (false), ignoring user-specified values in generated.

Move the flag assignments outside the conditional branches so they apply regardless of socket type:

         } else if (generated.hostname.get()) |hostname| {
             bun.assertf(hostname.length() > 0, "truthy bindgen string shouldn't be empty", .{});
             result.hostname_or_unix = hostname.toUTF8(bun.default_allocator);
             const slice = result.hostname_or_unix.slice();
             result.port = generated.port orelse bun.URL.parse(slice).getPort() orelse {
                 return global.throwInvalidArguments("Missing \"port\"", .{});
             };
-            result.exclusive = generated.exclusive;
-            result.allowHalfOpen = generated.allow_half_open;
-            result.reusePort = generated.reuse_port;
-            result.ipv6Only = generated.ipv6_only;
         } else {
             return global.throwInvalidArguments("Expected either \"hostname\" or \"unix\"", .{});
         }
+        result.exclusive = generated.exclusive;
+        result.allowHalfOpen = generated.allow_half_open;
+        result.reusePort = generated.reuse_port;
+        result.ipv6Only = generated.ipv6_only;
         return result;

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 909bb68 and b044163.

📒 Files selected for processing (2)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/codegen/bindgenv2/internal/enumeration.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
🧠 Learnings (2)
📚 Learning: 2025-10-17T20:50:58.608Z
Learnt from: taylordotfish
PR: oven-sh/bun#23755
File: src/bun.js/api/bun/socket/Handlers.zig:154-159
Timestamp: 2025-10-17T20:50:58.608Z
Learning: In Bun socket configuration error messages (src/bun.js/api/bun/socket/Handlers.zig), use the user-facing JavaScript names "data" and "drain" instead of internal field names "onData" and "onWritable", as these are the names users see in the API according to SocketConfig.bindv2.ts.

Applied to files:

  • src/bun.js/api/bun/socket/Handlers.zig
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (11)
src/bun.js/api/bun/socket/Handlers.zig (6)

21-33: LGTM: Clean CI-only assertion tracking and DRY callback iteration.

The protection_count field provides zero-cost runtime validation in CI builds, and the callback_fields tuple eliminates repetition in protect/unprotect/withAsyncContextIfNeeded/clone logic.


92-95: LGTM: Allocator mismatch resolved.

Using vm.allocator.destroy(this) correctly matches the allocator used during handler allocation.


118-163: LGTM: Handlers initialization with proper validation and protection.

The bindgen-based initialization correctly validates callbacks, ensures at least one data/drain handler exists, wraps with async context, and protects JSValues before returning.


165-227: LGTM: Comprehensive lifecycle management with symmetric protection tracking.

The deinit/unprotect/protect implementations correctly manage JSValue lifetimes, and clone() appropriately creates a shallow copy with independent protection for reusing handler callbacks across connections.


242-257: LGTM: Careful separation of handlers lifecycle from SocketConfig.

The deinitExcludingHandlers pattern correctly preserves the handlers field after deinitialization, supporting scenarios where handlers outlive the SocketConfig (e.g., connection handlers remaining active after config is freed).


329-338: LGTM: Clean delegation to generated bindings with proper cleanup.

The fromJS wrapper correctly handles temporary generated object lifecycle.

src/codegen/bindgenv2/internal/enumeration.ts (5)

39-52: C++ member synthesis with collision suffixing LGTM.

Name normalization and deterministic suffixing handle collisions cleanly and preserve index order.


68-73: toCpp lookup and error path LGTM.

Index resolution via valueMap and range validation are correct; mapping to cppMembers by index is consistent with uniqueValues ordering.


79-91: Human‑readable name construction LGTM.

Using uniqueValues and deferring escaping to toQuotedLiteral is correct and safe.


143-155: C++ mappings generation LGTM.

Compile‑time array size from valueMap.size and use of WTF::SortedArrayMap are appropriate.


183-185: Zig enum emission LGTM.

Using @"..." for enumerators derived from uniqueValues matches Zig’s quoted identifiers and keeps indices aligned.

Comment thread src/codegen/bindgenv2/internal/enumeration.ts
Comment thread src/codegen/bindgenv2/internal/enumeration.ts
Comment thread src/codegen/bindgenv2/internal/enumeration.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)

38-52: Avoid side‑effects in Set.add during uniqueness checks.

Current pattern mutates in the conditional, which is harder to read/maintain. Use an explicit check; still one insertion per iteration.

-  const cppMemberSet = new Set<string>();
-  for (const value of uniqueValues) {
-    let cppName = "k";
-    cppName += value
-      .split(/[^A-Za-z0-9]+/)
-      .filter(x => x)
-      .map(s => s[0].toUpperCase() + s.slice(1))
-      .join("");
-    if (cppMemberSet.size === cppMemberSet.add(cppName).size) {
-      let i = 2;
-      while (cppMemberSet.size === cppMemberSet.add(cppName + i).size) {
-        ++i;
-      }
-    }
-  }
-  const cppMembers = Array.from(cppMemberSet);
+  const cppMembers: string[] = [];
+  const seen = new Set<string>();
+  for (const value of uniqueValues) {
+    let base = "k" + value.split(/[^A-Za-z0-9]+/).filter(Boolean).map(s => s[0].toUpperCase() + s.slice(1)).join("");
+    let name_ = base;
+    let i = 2;
+    while (seen.has(name_)) name_ = base + i++;
+    seen.add(name_);
+    cppMembers.push(name_);
+  }
♻️ Duplicate comments (3)
src/codegen/bindgenv2/internal/enumeration.ts (3)

18-24: API shape is good; tighten types with readonly for stricter TS.

Make the inputs/derived arrays readonly to prevent mutation and align with STAB‑1476.

-export function enumeration(name: string, values: (string | string[])[]): EnumType {
-  const uniqueValues = values.map((v, i) => {
+export function enumeration(name: string, values: readonly (string | readonly string[])[]): EnumType {
+  const uniqueValues: readonly string[] = values.map((v, i) => {
     if (!Array.isArray(v)) return v;
     if (v.length === 0) throw RangeError(`enum value cannot be empty (index ${i})`);
     return v[0];
   });

28-36: Build valueMap in one pass and include enum name in the error.

Cuts transient allocations and improves diagnostics without extra lookups.

-  const indexedValues = values
-    .map(v => (Array.isArray(v) ? v : [v]))
-    .flatMap((arr, i) => arr.map((v): [string, number] => [v, i]));
-  const valueMap = new Map<string, number>();
-  for (const [value, index] of indexedValues) {
-    if (valueMap.size === valueMap.set(value, index).size) {
-      throw RangeError(`duplicate enum value: ${util.inspect(value)}`);
-    }
-  }
+  const valueMap = new Map<string, number>();
+  for (let i = 0; i < values.length; i++) {
+    const aliases = Array.isArray(values[i]) ? (values[i] as readonly string[]) : [values[i] as string];
+    for (const alias of aliases) {
+      if (valueMap.size === valueMap.set(alias, i).size) {
+        throw RangeError(`duplicate enum value in ${name}: ${util.inspect(alias)}`);
+      }
+    }
+  }

146-147: Comparator should return 0 on equality (defensive correctness).

Prevents violating the comparator contract if equals ever slip through.

-                .sort(([v1, i1], [v2, i2]) => (v1 < v2 ? -1 : 1))
+                .sort(([v1], [v2]) => (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0)))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b044163 and ee5fd2d.

📒 Files selected for processing (1)
  • src/codegen/bindgenv2/internal/enumeration.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
🔇 Additional comments (3)
src/codegen/bindgenv2/internal/enumeration.ts (3)

79-91: LGTM on human‑readable list generation.

The quoted display and fallback formatting look correct.


165-171: Verify ToString vs isString strictness matches intended semantics.

This specialization uses value.toWTFString(&globalObject), which coerces non‑strings. Prior bindgenv2 design (JSDOMConvertEnumeration) favored requiring isString() without coercion. Confirm this path is intentionally looser (or adjust).

Would you like me to scan the repo to list all call sites using these specializations to confirm behavior parity across backends?


181-185: Zig emission looks correct.

Using @"literal" for enum tags is valid and keeps tags lossless.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/codegen/bindgenv2/internal/enumeration.ts (2)

149-150: Comparator should return 0 for equality (defensive correctness).

While duplicates are prevented earlier, the comparator should conform to the standard contract by returning 0 when values are equal. This ensures correctness if the code is refactored in the future.

Apply this diff:

               Array.from(valueMap.entries())
-                .sort(([v1], [v2]) => (v1 < v2 ? -1 : 1))
+                .sort(([v1], [v2]) => (v1 < v2 ? -1 : v1 > v2 ? 1 : 0))
                 .map(([value, i]) => {

31-39: Consider enhancing the error message with enum name context.

The duplicate detection logic is correct and efficient. However, including the enum name in the error message would improve debuggability when multiple enumerations are processed.

Optional improvement (preserving your efficient one-lookup approach):

   for (const [value, index] of indexedValues) {
     if (valueMap.size === valueMap.set(value, index).size) {
-      throw RangeError(`duplicate enum value: ${util.inspect(value)}`);
+      throw RangeError(`duplicate enum value in ${name}: ${util.inspect(value)}`);
     }
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ee5fd2d and 1921806.

📒 Files selected for processing (1)
  • src/codegen/bindgenv2/internal/enumeration.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
PR: oven-sh/bun#23169
File: src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h:47-74
Timestamp: 2025-10-01T21:59:54.571Z
Learning: In the new bindings generator (bindgenv2) for `src/bun.js/bindings/webcore/JSDOMConvertEnumeration.h`, the context-aware enumeration conversion overloads intentionally use stricter validation (requiring `value.isString()` without ToString coercion), diverging from Web IDL semantics. This is a design decision documented in comments.

Applied to files:

  • src/codegen/bindgenv2/internal/enumeration.ts
🧬 Code graph analysis (1)
src/codegen/bindgenv2/internal/enumeration.ts (1)
src/codegen/bindgenv2/internal/base.ts (2)
  • joinIndented (123-125)
  • toQuotedLiteral (127-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (2)
src/codegen/bindgenv2/internal/enumeration.ts (2)

14-26: LGTM: Alias array support is well-designed.

The function signature and uniqueValues extraction correctly implement support for multiple string aliases mapping to the same enum value (STAB-1472). The JSDoc clearly explains the behavior, and the empty-array validation provides helpful context.


82-82: LGTM: Code generation correctly uses canonical values.

The C++ header and Zig source generation both correctly use uniqueValues to emit one enum member per unique value, while the C++ source mappings use valueMap to ensure all aliases resolve to the correct member. This architecture properly implements the alias feature.

Also applies to: 186-186

Comment thread src/codegen/bindgenv2/internal/enumeration.ts
Comment thread src/bun.js/api/bun/socket/Handlers.zig Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/bun.js/api/bun/socket/Handlers.zig (1)

314-327: Move socket configuration flags outside conditional branch to apply to all socket types.

The flags exclusive, allowHalfOpen, reusePort, and ipv6Only are set from the generated config only when the hostname branch is taken (lines 321-324). When unix_ or fd paths are used, these configuration values are silently ignored and the fields retain their default false values.

The socketFlags() method uses all four flags unconditionally, indicating they should apply to any socket type. The transport layer also supports these flags—allowHalfOpen is passed to both TCP and Unix domain socket connect methods.

Move the flag assignments outside the if-else chain to ensure generated config values are captured for all socket types:

         } else if (generated.hostname.get()) |hostname| {
             bun.assertf(hostname.length() > 0, "truthy bindgen string shouldn't be empty", .{});
             result.hostname_or_unix = hostname.toUTF8(bun.default_allocator);
             const slice = result.hostname_or_unix.slice();
             result.port = generated.port orelse bun.URL.parse(slice).getPort() orelse {
                 return global.throwInvalidArguments("Missing \"port\"", .{});
             };
-            result.exclusive = generated.exclusive;
-            result.allowHalfOpen = generated.allow_half_open;
-            result.reusePort = generated.reuse_port;
-            result.ipv6Only = generated.ipv6_only;
         } else {
             return global.throwInvalidArguments("Expected either \"hostname\" or \"unix\"", .{});
         }
+        result.exclusive = generated.exclusive;
+        result.allowHalfOpen = generated.allow_half_open;
+        result.reusePort = generated.reuse_port;
+        result.ipv6Only = generated.ipv6_only;
         return result;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 718161a and 2a8ad7a.

📒 Files selected for processing (3)
  • src/bun.js/api/bun/socket/Handlers.zig (7 hunks)
  • src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1 hunks)
  • src/codegen/bindgenv2/internal/string.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

Files:

  • src/bun.js/api/bun/socket/Handlers.zig
🧠 Learnings (1)
📚 Learning: 2025-10-17T20:50:58.644Z
Learnt from: taylordotfish
PR: oven-sh/bun#23755
File: src/bun.js/api/bun/socket/Handlers.zig:154-159
Timestamp: 2025-10-17T20:50:58.644Z
Learning: In Bun socket configuration error messages (src/bun.js/api/bun/socket/Handlers.zig), use the user-facing JavaScript names "data" and "drain" instead of internal field names "onData" and "onWritable", as these are the names users see in the API according to SocketConfig.bindv2.ts.

Applied to files:

  • src/bun.js/api/bun/socket/SocketConfig.bindv2.ts
  • src/bun.js/api/bun/socket/Handlers.zig
🧬 Code graph analysis (2)
src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (1)
src/bun.js/api/server/SSLConfig.bindv2.ts (1)
  • SSLConfig (23-90)
src/codegen/bindgenv2/internal/string.ts (2)
src/codegen/bindgen-lib.ts (1)
  • Type (18-32)
src/codegen/bindgenv2/internal/base.ts (1)
  • CodeStyle (5-5)
🔇 Additional comments (7)
src/codegen/bindgenv2/internal/string.ts (1)

28-28: ****

LooseString is properly exported. The code at line 28 explicitly includes the export const keyword: export const LooseString: Type = new (class extends Type {. No action needed.

Likely an incorrect or invalid review comment.

src/bun.js/api/bun/socket/SocketConfig.bindv2.ts (6)

1-3: LGTM!

The imports are clean and follow the project's conventions.


4-8: LGTM!

The binary type enumeration is well-defined with appropriate mappings between user-facing and internal names.


10-32: LGTM!

The handler mappings correctly use user-facing names ("data", "drain") as keys and map them to appropriate internal names ("onData", "onWritable"). The structure follows consistent conventions.

Based on learnings


34-38: LGTM!

The TLS union type correctly models the common pattern of allowing null, boolean, or full TLS configuration objects.


61-61: Verify the ordering of .loose and .nullable modifiers.

There's an inconsistency in the order of type modifiers:

  • Line 61: b.u16.loose.nullable (.loose before .nullable)
  • Line 78: b.String.nullable.loose (.loose after .nullable)

If the order affects the conversion behavior, this inconsistency may need to be addressed. Please verify whether:

  1. The ordering is intentional and produces different semantics
  2. The ordering should be consistent across all fields

If ordering doesn't matter, consider standardizing to one approach for consistency. If it does matter, please add comments explaining the semantic difference.

Also applies to: 78-78


79-79: Good documentation of the C macro workaround.

The comment explaining why unix_ is used instead of unix (to avoid conflicts with predefined C macros) is helpful for future maintainers.

Comment thread src/bun.js/api/bun/socket/SocketConfig.bindv2.ts
Comment thread src/codegen/bindgenv2/internal/string.ts
Comment thread src/codegen/bindgenv2/internal/string.ts
@Jarred-Sumner Jarred-Sumner merged commit 4a06991 into main Oct 19, 2025
59 of 60 checks passed
@Jarred-Sumner Jarred-Sumner deleted the taylor.fish/socket-config branch October 19, 2025 01:14
@coderabbitai coderabbitai Bot mentioned this pull request Jan 15, 2026
5 tasks
Jarred-Sumner pushed a commit that referenced this pull request May 4, 2026
…30150)

## Repro

Windows only:
```js
await Bun.connect({
  unix: '\\\\.\\pipe\\x',
  socket: { data() {}, open() {}, close() {} },
});
// then close (or fail) the connection
```

On close, `Handlers.markInactive()` hits `active_connections == 0` with
`.mode == .server` and does `@fieldParentPtr("handlers", this)`
expecting an enclosing `Listener` — but the handlers live in a
standalone `allocator.create(Handlers)` block, so reading
`listen_socket.listener` falls past the allocation. Under ASAN that's a
heap-buffer-overflow; on release it reads garbage and — because the
`.client` branch is skipped — leaks the block.

## Cause

`connectInner()` calls `SocketConfig.fromJS(vm, opts, globalObject,
true)` at `Listener.zig:564`. The last argument is `is_server`, which
feeds `handlers.mode`. It was `false` until 4a06991 (#23755) flipped
it during a bindings-generator refactor.

The non-pipe path at :797 has always had an explicit `handlers_ptr.mode
= .client` after copying into the heap block (it was
`handlers_ptr.is_server = false` before #26539), which masked the flip
everywhere except the Windows named-pipe early-return at :655–656, which
never had one.

`is_server` is only used to set `handlers.mode`; nothing else in
`SocketConfig.fromGenerated` / `Handlers.fromGenerated` branches on it.

## Fix

- Restore `is_server=false` at the `connectInner` call site (this is the
client connect path).
- Add the same defensive `handlers_ptr.mode = .client` on the named-pipe
branch to mirror the non-pipe branch, so the two copies into a
standalone `Handlers` block look the same.

Audited the other standalone `allocator.create(Handlers)` sites:
- `socket.zig:1557` — sourced from `Handlers.fromJS(..., false)`,
already `.client`.
- `socket.zig:2062` — explicit `.mode = if (is_server) .duplex_server
else .client`.

## Verification

`bun run zig:check-all` passes (all targets, including both Windows
arches).

New Windows-only tests in `test/js/bun/net/socket.test.ts`:
- Listen on a named pipe, `Bun.connect` to it, close → clean exit.
- `Bun.connect` to a non-existent pipe → rejects, clean exit.

Both are spawned in a subprocess so an ASAN crash surfaces as a non-zero
exit instead of killing the test runner. Skipped on non-Windows (the `if
(Environment.isWindows)` branch is unreachable there, and the non-pipe
path's :797 override already covers it).

---------

Co-authored-by: robobun <robobun@users.noreply.github.com>
xhjkl pushed a commit to xhjkl/bun that referenced this pull request May 14, 2026
…ven-sh#30150)

## Repro

Windows only:
```js
await Bun.connect({
  unix: '\\\\.\\pipe\\x',
  socket: { data() {}, open() {}, close() {} },
});
// then close (or fail) the connection
```

On close, `Handlers.markInactive()` hits `active_connections == 0` with
`.mode == .server` and does `@fieldParentPtr("handlers", this)`
expecting an enclosing `Listener` — but the handlers live in a
standalone `allocator.create(Handlers)` block, so reading
`listen_socket.listener` falls past the allocation. Under ASAN that's a
heap-buffer-overflow; on release it reads garbage and — because the
`.client` branch is skipped — leaks the block.

## Cause

`connectInner()` calls `SocketConfig.fromJS(vm, opts, globalObject,
true)` at `Listener.zig:564`. The last argument is `is_server`, which
feeds `handlers.mode`. It was `false` until 4a06991 (oven-sh#23755) flipped
it during a bindings-generator refactor.

The non-pipe path at :797 has always had an explicit `handlers_ptr.mode
= .client` after copying into the heap block (it was
`handlers_ptr.is_server = false` before oven-sh#26539), which masked the flip
everywhere except the Windows named-pipe early-return at :655–656, which
never had one.

`is_server` is only used to set `handlers.mode`; nothing else in
`SocketConfig.fromGenerated` / `Handlers.fromGenerated` branches on it.

## Fix

- Restore `is_server=false` at the `connectInner` call site (this is the
client connect path).
- Add the same defensive `handlers_ptr.mode = .client` on the named-pipe
branch to mirror the non-pipe branch, so the two copies into a
standalone `Handlers` block look the same.

Audited the other standalone `allocator.create(Handlers)` sites:
- `socket.zig:1557` — sourced from `Handlers.fromJS(..., false)`,
already `.client`.
- `socket.zig:2062` — explicit `.mode = if (is_server) .duplex_server
else .client`.

## Verification

`bun run zig:check-all` passes (all targets, including both Windows
arches).

New Windows-only tests in `test/js/bun/net/socket.test.ts`:
- Listen on a named pipe, `Bun.connect` to it, close → clean exit.
- `Bun.connect` to a non-existent pipe → rejects, clean exit.

Both are spawned in a subprocess so an ASAN crash surfaces as a non-zero
exit instead of killing the test runner. Skipped on non-Windows (the `if
(Environment.isWindows)` branch is unreachable there, and the non-pipe
path's :797 override already covers it).

---------

Co-authored-by: robobun <robobun@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants