## Speculative fix for
[BUN-Q81](https://bun-p9.sentry.io/issues/BUN-Q81)
BUN-Q81 is a long-standing `SlotVisitor::drain` segfault during GC
marking (150 occurrences since July 2025, across v1.1.10 through
v1.3.10). A full audit of the codebase for GC safety issues found three
bugs:
### 1. `JSCommonJSModule::m_overriddenCompile` not visited in
`visitChildren`
`m_overriddenCompile` is a `WriteBarrier<Unknown>` that stores the
overridden `module._compile` function (used by `ts-node`, `pirates`,
`@swc-node/register`, etc.). It was the only WriteBarrier field in the
class not visited by `visitChildrenImpl`, making it invisible to the GC.
The pointed-to function could be prematurely collected, and subsequent
GC marking would follow the dangling WriteBarrier pointer into freed
memory.
**This is the strongest candidate for BUN-Q81.**
### 2. `JSSQLStatement::userPrototype` — wrong owner in
`WriteBarrier::set()`
```cpp
// Before (wrong):
castedThis->userPrototype.set(vm, classObject, prototype.getObject());
// After (correct):
castedThis->userPrototype.set(vm, castedThis, prototype.getObject());
```
The owner parameter must be the object containing the WriteBarrier so
the GC's remembered set is updated correctly. All other `.set()` calls
in the same file correctly use `castedThis`.
### 3. `NodeVMSpecialSandbox` — missing `visitChildren` entirely
`NodeVMSpecialSandbox` has a `WriteBarrier<NodeVMGlobalObject>
m_parentGlobal` member but had no `visitChildren` implementation. Added
the standard boilerplate.
- WTFMove → WTF::move / std::move: Replaced WTFMove() macro with
WTF::move() function for WTF types, std::move() for std types
- SortedArrayMap removed: Replaced with if-else chains in
EventFactory.cpp, JSCryptoKeyUsage.cpp
- Wasm::Memory::create signature changed: Removed VM parameter
- URLPattern allocation: Changed from WTF_MAKE_ISO_ALLOCATED to
WTF_MAKE_TZONE_ALLOCATED
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## 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`:
```cpp
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.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
<!-- **Please explain what your changes do**, example: -->
<!--
This adds a new flag --bail to bun test. When set, it will stop running
tests after the first failure. This is useful for CI environments where
you want to fail fast.
-->
### How did you verify your code works?
ran fuzzy-wuzzy.test.ts
<!-- **For code changes, please include automated tests**. Feel free to
uncomment the line below -->
<!-- I wrote automated tests -->
<!-- If JavaScript/TypeScript modules or builtins changed:
- [ ] I included a test for the new code, or existing tests cover it
- [ ] I ran my tests locally and they pass (`bun-debug test
test-file-name.test`)
-->
<!-- If Zig files changed:
- [ ] I checked the lifetime of memory allocated to verify it's (1)
freed and (2) only freed when it should be
- [ ] I included a test for the new code, or an existing test covers it
- [ ] JSValue used outside of the stack is either wrapped in a
JSC.Strong or is JSValueProtect'ed
- [ ] I wrote TypeScript/JavaScript tests and they pass locally
(`bun-debug test test-file-name.test`)
-->
<!-- If new methods, getters, or setters were added to a publicly
exposed class:
- [ ] I added TypeScript types for the new methods, getters, or setters
-->
<!-- If dependencies in tests changed:
- [ ] I made sure that specific versions of dependencies are used
instead of ranged or tagged versions
-->
<!-- If a new builtin ESM/CJS module was added:
- [ ] I updated Aliases in `module_loader.zig` to include the new module
- [ ] I added a test that imports the module
- [ ] I added a test that require() the module
-->