Skip to content

Fix WriteBarrier initialization to use WriteBarrierEarlyInit - #23435

Merged
Jarred-Sumner merged 6 commits into
mainfrom
claude/fix-writebarrier-earlyinit
Oct 10, 2025
Merged

Jarred-Sumner merged 6 commits into
mainfrom
claude/fix-writebarrier-earlyinit

Conversation

@robobun

@robobun robobun commented Oct 10, 2025

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes incorrect WriteBarrier initialization patterns throughout the Bun codebase where .set() or .setEarlyValue() was being called in the constructor body or in finishCreation(). According to JavaScriptCore's WriteBarrier.h, WriteBarriers that are initialized during construction should use the WriteBarrierEarlyInit constructor in the initializer list to avoid triggering unnecessary write barriers.

Changes

The following classes were updated to properly initialize WriteBarrier fields:

  1. InternalModuleRegistry - Initialize internal fields in constructor using setWithoutWriteBarrier() instead of calling .set() in finishCreation()
  2. AsyncContextFrame - Pass callback and context to constructor and use WriteBarrierEarlyInit
  3. JSCommonJSModule - Pass id, filename, dirname to constructor and use WriteBarrierEarlyInit
  4. JSMockImplementation - Pass initial values to constructor and use WriteBarrierEarlyInit
  5. JSConnectionsList - Pass connection sets to constructor and use WriteBarrierEarlyInit
  6. JSBunRequest - Pass params to constructor and initialize both m_params and m_cookies using WriteBarrierEarlyInit
  7. JSNodeHTTPServerSocket - Initialize currentResponseObject using WriteBarrierEarlyInit instead of calling setEarlyValue()

Why This Matters

From JavaScriptCore's WriteBarrier.h:

enum WriteBarrierEarlyInitTag { WriteBarrierEarlyInit };

// Constructor for early initialization during object construction
WriteBarrier(T* value, WriteBarrierEarlyInitTag)
{
    this->setWithoutWriteBarrier(value);
}

Using WriteBarrierEarlyInit during construction:

  • Avoids triggering write barriers when they're not needed
  • Is the correct pattern for initializing WriteBarriers before the object is fully constructed
  • Aligns with JavaScriptCore best practices

Testing

  • ✅ Full debug build completes successfully
  • ✅ Basic functionality tested (CommonJS modules, HTTP requests, Node HTTP servers)
  • ✅ No regressions observed

Note on generate-classes.ts

The code generator (generate-classes.ts) does not need updates because generated classes intentionally leave WriteBarrier fields (callbacks, cached fields, values) uninitialized. They start with default-constructed WriteBarriers and are populated later by Zig code via setter functions, which is the correct pattern for those fields.

@robobun

robobun commented Oct 10, 2025 •

Copy link
Copy Markdown
Collaborator Author
Updated 4:34 AM PT - Oct 10th, 2025

❌ Your commit be08df34 has 2 failures in Build #28794 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 23435

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

bun-23435 --bun

@coderabbitai

coderabbitai Bot commented Oct 10, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Multiple binding classes had field initialization moved into constructors (using WriteBarrierEarlyInit), constructor signatures changed to accept new JSValue members, related create/finishCreation call sites were updated, new accessors/members added, and some initialization routines made defensive or simplified.

Changes

Cohort / File(s) Summary of changes
Constructor-based initialization refactor
src/bun.js/bindings/AsyncContextFrame.cpp, src/bun.js/bindings/AsyncContextFrame.h, src/bun.js/bindings/JSBunRequest.cpp, src/bun.js/bindings/JSBunRequest.h, src/bun.js/bindings/JSCommonJSModule.cpp, src/bun.js/bindings/JSCommonJSModule.h, src/bun.js/bindings/JSMockFunction.cpp, src/bun.js/bindings/node/http/JSConnectionsList.cpp, src/bun.js/bindings/node/http/JSConnectionsList.h, src/bun.js/bindings/BunPlugin.cpp, src/bun.js/bindings/JSWrappingFunction.cpp, src/bun.js/bindings/JSWrappingFunction.h, src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp
Constructors were extended to accept and initialize additional JSValue/object members (callback, context, params, id/filename/dirname, wrappedFn, JSSet refs, etc.) via WriteBarrierEarlyInit; corresponding create and finishCreation call sites/signatures were simplified or adjusted; new accessors and private members added for connections.
InternalModuleRegistry initialization change
src/bun.js/bindings/InternalModuleRegistry.cpp
Internal module slots are initialized to jsUndefined() in the constructor with setWithoutWriteBarrier(); redundant initialization in finishCreation was removed.
Node VM module / import.meta handling
src/bun.js/bindings/NodeVMSourceTextModule.cpp, src/bun.js/bindings/NodeVMSourceTextModule.h, src/bun.js/bindings/NodeVMModule.cpp
NodeVMSourceTextModule gained an initializeImportMeta parameter/member and guards against missing/non-object metaValue; NodeVMModule moved m_context initialization into the initializer list using WriteBarrierEarlyInit.

Possibly related PRs

Suggested reviewers

  • Jarred-Sumner
  • cirospaciari
  • zackradisic

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description does not follow the repository’s required template headings; it uses custom sections like “## Summary” and “## Testing” instead of the mandated “### What does this PR do?” and “### How did you verify your code works?”. Please update the pull request description to include the required sections "### What does this PR do?" and "### How did you verify your code works?" and move the corresponding content under those headings to comply with the repository template.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely describes the primary change of the pull request by indicating that WriteBarrier initialization patterns have been updated to use WriteBarrierEarlyInit, which aligns with the bulk of the modifications described in the changeset.

📜 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 a8ada84df0b2639284680db0419ffaef3db4a8cb and be08df3.

📒 Files selected for processing (3)
  • src/bun.js/bindings/NodeVMModule.cpp (1 hunks)
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp (3 hunks)
  • src/bun.js/bindings/NodeVMSourceTextModule.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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/NodeVMSourceTextModule.h
  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
**/*.cpp

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

**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue

Files:

  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
src/bun.js/bindings/**/*.cpp

📄 CodeRabbit inference engine (CLAUDE.md)

src/bun.js/bindings/**/*.cpp: When a JS class has a public constructor, implement Foo, FooPrototype, and FooConstructor (JSDestructibleObject, JSNonFinalObject, InternalFunction)
Define class properties using HashTableValue arrays in C++ bindings
Add iso subspaces for classes with C++ fields
Cache structures in ZigGlobalObject for C++-backed classes

Files:

  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
🧬 Code graph analysis (2)
src/bun.js/bindings/NodeVMSourceTextModule.h (1)
src/bun.js/bindings/NodeVMSourceTextModule.cpp (2)
  • initializeImportMeta (416-442)
  • initializeImportMeta (416-416)
src/bun.js/bindings/NodeVMSourceTextModule.cpp (2)
src/bun.js/bindings/JSCommonJSModule.cpp (6)
  • vm (761-769)
  • vm (761-764)
  • vm (771-779)
  • vm (771-774)
  • vm (797-815)
  • vm (797-797)
src/bun.js/bindings/NodeVMScript.cpp (4)
  • vm (608-613)
  • vm (608-608)
  • vm (622-625)
  • vm (622-622)
⏰ 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 (4)
src/bun.js/bindings/NodeVMModule.cpp (1)

152-158: LGTM! Correct WriteBarrierEarlyInit usage.

The initialization of m_context in the member initializer list properly guards against invalid context values and uses WriteBarrierEarlyInit as intended.

src/bun.js/bindings/NodeVMSourceTextModule.h (1)

60-62: LGTM! Constructor correctly initializes with WriteBarrierEarlyInit.

The initialization logic properly normalizes both empty and undefined initializeImportMeta values to an empty JSValue(), and the use of WriteBarrierEarlyInit aligns with the PR's objective to move initialization into constructors.

src/bun.js/bindings/NodeVMSourceTextModule.cpp (2)

108-110: LGTM! Constructor call properly formatted.

The multi-line formatting improves readability, and all parameters are correctly passed to the constructor, including the new initializeImportMeta parameter.


416-432: LGTM! Defensive checks improve robustness.

Two improvements here:

  1. Line 418 adds a null check before accessing m_initializeImportMeta, which is necessary since it can now be empty.
  2. Lines 430-432 replace an assertion with an early return, making the code more defensive and suitable for production.

Both changes enhance robustness without altering the intended behavior.


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

This fixes incorrect WriteBarrier initialization patterns where `.set()`
or `.setEarlyValue()` was being called in the constructor body or in
`finishCreation()`. According to JavaScriptCore's WriteBarrier.h,
WriteBarriers that are initialized during construction should use the
WriteBarrierEarlyInit constructor in the initializer list to avoid
triggering unnecessary write barriers.

Changes:
- InternalModuleRegistry: Initialize internal fields in constructor using
  setWithoutWriteBarrier instead of calling .set() in finishCreation
- AsyncContextFrame: Pass callback and context to constructor and use
  WriteBarrierEarlyInit
- JSCommonJSModule: Pass id, filename, dirname to constructor and use
  WriteBarrierEarlyInit
- JSMockImplementation: Pass initial values to constructor and use
  WriteBarrierEarlyInit
- JSConnectionsList: Pass connection sets to constructor and use
  WriteBarrierEarlyInit
- JSBunRequest: Pass params to constructor and initialize both m_params
  and m_cookies using WriteBarrierEarlyInit
- JSNodeHTTPServerSocket: Initialize currentResponseObject using
  WriteBarrierEarlyInit instead of calling setEarlyValue()
- JSModuleMock: Pass callback to constructor and use WriteBarrierEarlyInit
- JSWrappingFunction: Pass wrappedFn to constructor and use WriteBarrierEarlyInit
- NodeVMSourceTextModule: Pass initializeImportMeta to constructor and use
  WriteBarrierEarlyInit
- NodeVMModule: Initialize m_context in initializer list using WriteBarrierEarlyInit

Note: CallSite was identified but not fixed as it requires more complex
refactoring due to runtime-dependent initialization logic.

The generate-classes.ts code generator does not need updates because
generated classes intentionally leave WriteBarrier fields uninitialized,
to be populated later by Zig code via setter functions.
@robobun
robobun force-pushed the claude/fix-writebarrier-earlyinit branch from cd11942 to 175cc35 Compare October 10, 2025 05:43

@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

📜 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 cd11942869ba5d3248604616c3828d3e95639c24 and 175cc35.

📒 Files selected for processing (17)
  • src/bun.js/bindings/AsyncContextFrame.cpp (2 hunks)
  • src/bun.js/bindings/AsyncContextFrame.h (1 hunks)
  • src/bun.js/bindings/BunPlugin.cpp (1 hunks)
  • src/bun.js/bindings/InternalModuleRegistry.cpp (1 hunks)
  • src/bun.js/bindings/JSBunRequest.cpp (2 hunks)
  • src/bun.js/bindings/JSBunRequest.h (1 hunks)
  • src/bun.js/bindings/JSCommonJSModule.cpp (2 hunks)
  • src/bun.js/bindings/JSCommonJSModule.h (2 hunks)
  • src/bun.js/bindings/JSMockFunction.cpp (2 hunks)
  • src/bun.js/bindings/JSWrappingFunction.cpp (1 hunks)
  • src/bun.js/bindings/JSWrappingFunction.h (1 hunks)
  • src/bun.js/bindings/NodeVMModule.cpp (1 hunks)
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp (1 hunks)
  • src/bun.js/bindings/NodeVMSourceTextModule.h (1 hunks)
  • src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp (1 hunks)
  • src/bun.js/bindings/node/http/JSConnectionsList.cpp (1 hunks)
  • src/bun.js/bindings/node/http/JSConnectionsList.h (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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/InternalModuleRegistry.cpp
  • src/bun.js/bindings/AsyncContextFrame.h
  • src/bun.js/bindings/JSCommonJSModule.h
  • src/bun.js/bindings/BunPlugin.cpp
  • src/bun.js/bindings/JSBunRequest.h
  • src/bun.js/bindings/AsyncContextFrame.cpp
  • src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp
  • src/bun.js/bindings/JSWrappingFunction.cpp
  • src/bun.js/bindings/JSCommonJSModule.cpp
  • src/bun.js/bindings/JSMockFunction.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.h
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
  • src/bun.js/bindings/node/http/JSConnectionsList.cpp
  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/node/http/JSConnectionsList.h
  • src/bun.js/bindings/JSBunRequest.cpp
  • src/bun.js/bindings/JSWrappingFunction.h
**/*.cpp

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

**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue

Files:

  • src/bun.js/bindings/InternalModuleRegistry.cpp
  • src/bun.js/bindings/BunPlugin.cpp
  • src/bun.js/bindings/AsyncContextFrame.cpp
  • src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp
  • src/bun.js/bindings/JSWrappingFunction.cpp
  • src/bun.js/bindings/JSCommonJSModule.cpp
  • src/bun.js/bindings/JSMockFunction.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
  • src/bun.js/bindings/node/http/JSConnectionsList.cpp
  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/JSBunRequest.cpp
src/bun.js/bindings/**/*.cpp

📄 CodeRabbit inference engine (CLAUDE.md)

src/bun.js/bindings/**/*.cpp: When a JS class has a public constructor, implement Foo, FooPrototype, and FooConstructor (JSDestructibleObject, JSNonFinalObject, InternalFunction)
Define class properties using HashTableValue arrays in C++ bindings
Add iso subspaces for classes with C++ fields
Cache structures in ZigGlobalObject for C++-backed classes

Files:

  • src/bun.js/bindings/InternalModuleRegistry.cpp
  • src/bun.js/bindings/BunPlugin.cpp
  • src/bun.js/bindings/AsyncContextFrame.cpp
  • src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp
  • src/bun.js/bindings/JSWrappingFunction.cpp
  • src/bun.js/bindings/JSCommonJSModule.cpp
  • src/bun.js/bindings/JSMockFunction.cpp
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
  • src/bun.js/bindings/node/http/JSConnectionsList.cpp
  • src/bun.js/bindings/NodeVMModule.cpp
  • src/bun.js/bindings/JSBunRequest.cpp
🧠 Learnings (3)
📚 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 src/bun.js/bindings/**/*.cpp : When a JS class has a public constructor, implement Foo, FooPrototype, and FooConstructor (JSDestructibleObject, JSNonFinalObject, InternalFunction)

Applied to files:

  • src/bun.js/bindings/InternalModuleRegistry.cpp
  • src/bun.js/bindings/JSCommonJSModule.h
  • src/bun.js/bindings/BunPlugin.cpp
  • src/bun.js/bindings/JSBunRequest.h
📚 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 : Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors

Applied to files:

  • src/bun.js/bindings/BunPlugin.cpp
  • src/bun.js/bindings/JSBunRequest.h
  • src/bun.js/bindings/AsyncContextFrame.cpp
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to src/bun.js/bindings/ZigGlobalObject.{h,cpp} : If only a class (no public constructor/prototype), use JSC::LazyProperty<JSGlobalObject, Structure>, initialize it in finishCreation, and visit it in visitChildren

Applied to files:

  • src/bun.js/bindings/BunPlugin.cpp
🧬 Code graph analysis (10)
src/bun.js/bindings/AsyncContextFrame.h (1)
src/bun.js/bindings/BunPlugin.cpp (3)
  • vm (409-409)
  • vm (410-410)
  • callback (429-429)
src/bun.js/bindings/JSCommonJSModule.h (1)
src/bun.js/bindings/JSCommonJSModule.cpp (12)
  • finishCreation (408-426)
  • finishCreation (408-408)
  • finishCreation (820-825)
  • finishCreation (820-820)
  • finishCreation (1328-1335)
  • finishCreation (1328-1328)
  • vm (761-769)
  • vm (761-764)
  • vm (771-779)
  • vm (771-774)
  • vm (797-815)
  • vm (797-797)
src/bun.js/bindings/BunPlugin.cpp (2)
src/bun.js/bindings/JSBunRequest.cpp (10)
  • create (55-64)
  • create (55-55)
  • vm (182-187)
  • vm (182-182)
  • vm (189-194)
  • vm (189-189)
  • vm (211-216)
  • vm (211-211)
  • finishCreation (159-165)
  • finishCreation (159-159)
src/bun.js/bindings/NodeVMModule.cpp (10)
  • create (211-226)
  • create (211-211)
  • create (281-286)
  • create (281-281)
  • create (539-544)
  • create (539-539)
  • finishCreation (298-304)
  • finishCreation (298-298)
  • finishCreation (556-561)
  • finishCreation (556-556)
src/bun.js/bindings/JSBunRequest.h (1)
src/bun.js/bindings/JSBunRequest.cpp (10)
  • vm (182-187)
  • vm (182-182)
  • vm (189-194)
  • vm (189-189)
  • vm (211-216)
  • vm (211-211)
  • params (81-87)
  • params (81-81)
  • finishCreation (159-165)
  • finishCreation (159-159)
src/bun.js/bindings/JSCommonJSModule.cpp (1)
src/bun.js/bindings/JSCommonJSModule.h (1)
  • filename (130-130)
src/bun.js/bindings/JSMockFunction.cpp (3)
src/bun.js/bindings/BunPlugin.cpp (2)
  • vm (409-409)
  • vm (410-410)
src/bun.js/bindings/JSBunRequest.cpp (6)
  • vm (182-187)
  • vm (182-182)
  • vm (189-194)
  • vm (189-189)
  • vm (211-216)
  • vm (211-211)
src/bun.js/bindings/JSCommonJSModule.cpp (6)
  • vm (761-769)
  • vm (761-764)
  • vm (771-779)
  • vm (771-774)
  • vm (797-815)
  • vm (797-797)
src/bun.js/bindings/NodeVMSourceTextModule.h (1)
src/bun.js/bindings/NodeVMSourceTextModule.cpp (2)
  • initializeImportMeta (414-439)
  • initializeImportMeta (414-414)
src/bun.js/bindings/node/http/JSConnectionsList.cpp (2)
src/bun.js/bindings/JSBunRequest.cpp (8)
  • finishCreation (159-165)
  • finishCreation (159-159)
  • vm (182-187)
  • vm (182-182)
  • vm (189-194)
  • vm (189-189)
  • vm (211-216)
  • vm (211-211)
src/bun.js/bindings/JSCommonJSModule.cpp (12)
  • finishCreation (408-426)
  • finishCreation (408-408)
  • finishCreation (820-825)
  • finishCreation (820-820)
  • finishCreation (1328-1335)
  • finishCreation (1328-1328)
  • vm (761-769)
  • vm (761-764)
  • vm (771-779)
  • vm (771-774)
  • vm (797-815)
  • vm (797-797)
src/bun.js/bindings/node/http/JSConnectionsList.h (3)
src/bun.js/bindings/JSBunRequest.cpp (8)
  • vm (182-187)
  • vm (182-182)
  • vm (189-194)
  • vm (189-189)
  • vm (211-216)
  • vm (211-211)
  • finishCreation (159-165)
  • finishCreation (159-159)
src/bun.js/bindings/JSMockFunction.cpp (6)
  • vm (123-126)
  • vm (123-123)
  • globalObject (154-160)
  • globalObject (154-154)
  • finishCreation (981-988)
  • finishCreation (981-981)
src/bun.js/bindings/node/http/JSConnectionsList.cpp (2)
  • finishCreation (15-19)
  • finishCreation (15-15)
src/bun.js/bindings/JSBunRequest.cpp (1)
src/bun.js/bindings/JSBunRequest.h (1)
  • JSBunRequest (11-47)
🔇 Additional comments (33)
src/bun.js/bindings/JSMockFunction.cpp (3)

154-160: LGTM! Constructor initialization pattern correctly implemented.

The refactoring correctly moves WriteBarrier field initialization to the constructor. The conditional logic for isOnce (passing jsNumber(1) vs jsUndefined()) preserves the original behavior checked by the isOnce() method at lines 193-196.


198-204: Excellent adherence to JavaScriptCore best practices.

The constructor correctly uses WriteBarrierEarlyInit in the initializer list for both WriteBarrier fields. This avoids triggering unnecessary write barriers during object construction, which is the recommended pattern per JavaScriptCore guidelines.


206-209: Refactoring complete: post-construction initialization removed.

The simplified finishCreation method correctly reflects the completed refactoring. WriteBarrier field initialization has been successfully moved to the constructor, leaving only the required Base::finishCreation(vm) call.

src/bun.js/bindings/InternalModuleRegistry.cpp (2)

124-132: Initialization pattern looks correct.

Moving field initialization from finishCreation() to the constructor using setWithoutWriteBarrier() correctly avoids unnecessary write barriers during object construction. This aligns with JavaScriptCore best practices.


151-155: LGTM: finishCreation correctly simplified.

The removal of field initialization from finishCreation() is appropriate, as these fields are now initialized in the constructor where write barriers are not needed.

src/bun.js/bindings/NodeVMSourceTextModule.h (1)

60-62: LGTM! Correct use of WriteBarrierEarlyInit.

The constructor signature and member initialization correctly follow the JSC pattern of using WriteBarrierEarlyInit for early initialization. This avoids unnecessary write barriers during object construction.

src/bun.js/bindings/NodeVMSourceTextModule.cpp (1)

414-439: LGTM! Visitor and usage correctly handle the refactored member.

The initializeImportMeta() function correctly checks for null/undefined before using m_initializeImportMeta, and the visitor properly tracks the field for garbage collection.

Also applies to: 458-458

src/bun.js/bindings/JSWrappingFunction.h (2)

62-64: LGTM! WriteBarrierEarlyInit pattern correctly applied.

The constructor now properly initializes m_wrappedFn using WriteBarrierEarlyInit in the initializer list, avoiding unnecessary write barriers during object construction.


62-64: LGTM! Correct use of WriteBarrierEarlyInit.

The constructor correctly initializes m_wrappedFn using WriteBarrierEarlyInit in the initializer list, avoiding unnecessary write barriers during object construction. This aligns with JavaScriptCore best practices.

src/bun.js/bindings/NodeVMModule.cpp (2)

155-155: LGTM! Conditional initialization correctly handled.

The m_context member is now initialized in the constructor's initializer list using WriteBarrierEarlyInit, with proper conditional logic to handle non-object contexts.


155-155: LGTM! Conditional initialization is safe.

The conditional initialization context.isObject() ? asObject(context) : nullptr with WriteBarrierEarlyInit is correct. WriteBarrier can safely store nullptr, and the logic properly validates the type before conversion.

src/bun.js/bindings/JSWrappingFunction.cpp (2)

36-36: LGTM! Constructor call properly updated.

The wrappedFn parameter is now correctly passed to the constructor, enabling initialization via WriteBarrierEarlyInit as declared in the header.


36-36: LGTM! Constructor call updated correctly.

The constructor instantiation correctly passes wrappedFn as the final parameter, matching the updated constructor signature in the header.

src/bun.js/bindings/JSCommonJSModule.cpp (4)

820-824: LGTM! finishCreation properly simplified.

The finishCreation method now correctly focuses only on post-construction tasks (setting sourceCode and calling Base::finishCreation), with member initialization moved to the constructor.


847-848: LGTM! Constructor and finishCreation properly coordinated.

The create method correctly instantiates JSCommonJSModule with id, filename, and dirname parameters, then calls the simplified finishCreation with only vm and sourceCode.


820-824: LGTM! finishCreation correctly simplified.

The finishCreation method has been appropriately simplified to only handle sourceCode initialization, with id, filename, and dirname now initialized in the constructor.


847-849: LGTM! Constructor and finishCreation calls updated correctly.

The create function correctly passes id, filename, and dirname to the constructor and only vm and sourceCode to finishCreation, matching the updated signatures.

src/bun.js/bindings/AsyncContextFrame.h (2)

60-64: LGTM! WriteBarrierEarlyInit pattern correctly applied.

The constructor properly initializes callback and context using WriteBarrierEarlyInit in the initializer list, avoiding unnecessary write barriers during construction.


60-64: LGTM! Correct WriteBarrierEarlyInit usage.

The constructor properly initializes both callback and context using WriteBarrierEarlyInit in the initializer list, following JavaScriptCore best practices for early initialization.

src/bun.js/bindings/AsyncContextFrame.cpp (4)

17-19: LGTM! Factory method properly updated.

The create method correctly passes callback and context to the constructor, enabling initialization via WriteBarrierEarlyInit.


27-29: LGTM! Factory method properly updated.

The overloaded create method correctly passes callback and context to the constructor, enabling initialization via WriteBarrierEarlyInit.


17-17: LGTM! Constructor instantiation updated correctly.

The constructor call correctly passes callback and context parameters, matching the updated constructor signature.


27-27: LGTM! Second constructor instantiation also correct.

This constructor instantiation also correctly passes the callback and context parameters.

src/bun.js/bindings/JSCommonJSModule.h (3)

154-160: LGTM! WriteBarrierEarlyInit pattern correctly applied.

The constructor now properly initializes m_id, m_filename, and m_dirname using WriteBarrierEarlyInit in the initializer list, avoiding unnecessary write barriers during object construction.


86-86: LGTM! finishCreation signature correctly simplified.

The finishCreation signature has been appropriately simplified, removing the id, filename, and dirname parameters that are now handled in the constructor.


154-160: LGTM! Constructor correctly initializes members with WriteBarrierEarlyInit.

The constructor properly initializes m_id, m_filename, and m_dirname using WriteBarrierEarlyInit in the initializer list, following JavaScriptCore best practices for early field initialization.

src/bun.js/bindings/JSBunRequest.h (2)

42-43: Use WriteBarrierEarlyInit for m_params

In JSBunRequest.cpp line 153, replace the initializer

- , m_params(params, JSC::WriteBarrierEarlyInit)
+ , m_params(JSC::WriteBarrierEarlyInit(params))
⛔ Skipped due to learnings
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to src/bun.js/bindings/ZigGlobalObject.{h,cpp} : If only a class (no public constructor/prototype), use JSC::LazyProperty<JSGlobalObject, Structure>, initialize it in finishCreation, and visit it in visitChildren

42-43: Constructor initializer list correctly initializes m_params and m_cookies using WriteBarrierEarlyInit.

No further modifications required.

src/bun.js/bindings/BunPlugin.cpp (1)

432-432: Correct WriteBarrier initialization pattern.

The constructor correctly initializes callbackFunctionOrCachedResult using WriteBarrierEarlyInit in the initializer list, avoiding unnecessary write barriers during object construction. This aligns perfectly with JavaScriptCore's recommended pattern.

Also applies to: 449-453

src/bun.js/bindings/JSBunRequest.cpp (1)

61-62: Excellent refactor of WriteBarrier initialization.

The changes correctly move WriteBarrier initialization from finishCreation to the constructor:

  • Both m_params and m_cookies are now initialized using WriteBarrierEarlyInit in the constructor's initializer list (lines 153-154)
  • The params parameter is passed through the constructor instead of finishCreation
  • finishCreation signature is properly simplified to only accept vm (line 159)

This is a clean, complete refactor that aligns with JavaScriptCore best practices and the PR's objectives.

Also applies to: 151-165

src/bun.js/bindings/node/JSNodeHTTPServerSocket.cpp (1)

96-102: Approve WriteBarrierEarlyInit usage. Verify that WriteBarrierEarlyInit is available via current includes—add

#include <JavaScriptCore/WriteBarrier.h>

if necessary to resolve any x64-musl or Windows build failures.

src/bun.js/bindings/node/http/JSConnectionsList.cpp (1)

15-19: Constructor now correctly covers barrier setup.

With the write barriers initialized in the ctor, this lean finishCreation is consistent and avoids redundant set calls.

src/bun.js/bindings/node/http/JSConnectionsList.h (1)

46-49: Early write‑barrier initialization matches JSC recommendations.

Passing both sets through the ctor and using WriteBarrierEarlyInit ensures the fields are ready before GC sees the object.

Comment thread src/bun.js/bindings/BunPlugin.cpp Resolved
Claude Bot and others added 3 commits October 10, 2025 05:49
- Remove unused callback parameter from JSModuleMock::finishCreation
- Fix comment in InternalModuleRegistry to match implementation
- Format long line in NodeVMSourceTextModule for readability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
When initializeImportMeta is undefined, pass JSValue() instead to
preserve the original behavior where the WriteBarrier would remain
uninitialized rather than being set to jsUndefined().

Fixes crash in test-vm-timeout-escape-promise-module.js

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

@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 c0667e3 and f2b6f27.

📒 Files selected for processing (1)
  • src/bun.js/bindings/NodeVMSourceTextModule.cpp (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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/NodeVMSourceTextModule.cpp
**/*.cpp

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

**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue

Files:

  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
src/bun.js/bindings/**/*.cpp

📄 CodeRabbit inference engine (CLAUDE.md)

src/bun.js/bindings/**/*.cpp: When a JS class has a public constructor, implement Foo, FooPrototype, and FooConstructor (JSDestructibleObject, JSNonFinalObject, InternalFunction)
Define class properties using HashTableValue arrays in C++ bindings
Add iso subspaces for classes with C++ fields
Cache structures in ZigGlobalObject for C++-backed classes

Files:

  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.294Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : When a JS class has a public constructor, implement Foo, FooPrototype, and FooConstructor (JSDestructibleObject, JSNonFinalObject, InternalFunction)
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to src/bun.js/bindings/ZigGlobalObject.{h,cpp} : If only a class (no public constructor/prototype), use JSC::LazyProperty<JSGlobalObject, Structure>, initialize it in finishCreation, and visit it in visitChildren

Applied to files:

  • src/bun.js/bindings/NodeVMSourceTextModule.cpp
⏰ 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/bun.js/bindings/NodeVMSourceTextModule.cpp (1)

108-110: Verify JSValue empty vs undefined and reproduce VM module test crashes

  • Constructor’s m_initializeImportMeta(initializeImportMeta, WriteBarrierEarlyInit) usage is correct.
  • The ternary initializeImportMeta.isUndefined() ? JSValue() : initializeImportMeta converts undefined into an empty JSValue(), not jsUndefined(). Confirm downstream code tolerates this semantic difference.
  • Reproduce the segfaults in the test-vm-module-* suite locally to ensure this change isn’t the root cause.

The initializeImportMeta field is optional and should remain uninitialized
when not provided. WriteBarrierEarlyInit is not appropriate for optional
fields - they should use the original pattern of conditional .set() after
finishCreation().

This reverts the NodeVMSourceTextModule changes back to the original
implementation where m_initializeImportMeta is only set if the value
is not undefined.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robobun
robobun force-pushed the claude/fix-writebarrier-earlyinit branch from 7f2ef8f to a8ada84 Compare October 10, 2025 08:41
When using WriteBarrierEarlyInit with optional JSValue fields, we must
check if the value exists before calling methods like isObject(). Added
context && check in NodeVMModule and initializeImportMeta && check in
NodeVMSourceTextModule.

Also improved initializeImportMeta to check if the callback is callable
before proceeding, and gracefully handle cases where metaValue is not
an object instead of asserting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robobun
robobun force-pushed the claude/fix-writebarrier-earlyinit branch from a8ada84 to be08df3 Compare October 10, 2025 08:55
@Jarred-Sumner
Jarred-Sumner merged commit 8197a5f into main Oct 10, 2025
57 of 60 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/fix-writebarrier-earlyinit branch October 10, 2025 10:53
@coderabbitai coderabbitai Bot mentioned this pull request Jan 17, 2026
2 of 3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants