fix(build): don't force -fuse-ld=lld on macOS#30871
Conversation
## Summary Refactored `FFICallbackFunctionWrapper` to inherit from `WTF::ThreadSafeRefCounted` for safe cross-thread memory management. Guarded `JSFunction` execution in `FFI_Callback_threadsafe_call` by taking a protected `Ref` to prevent use-after-free crashes when JSC garbage collection runs concurrently with native thread callbacks. Fixed a debug-build assertion failure (`isASCII`) in `initializeInternalModuleFromDisk` caused by non-ASCII characters in `BUN_DYNAMIC_JS_LOAD_PATH` by using `WTF::String::fromUTF8` instead of `ASCIILiteral`. Added a high-stress test case simulating native thread callback execution with forced `Bun.gc(true)` to ensure thread safety. ## Test plan `bun bd test test/js/bun/ffi/cc.test.ts` passes reliably under high callback volume and forced garbage collection.
Homebrew's llvm@21 clang++ does not support -fuse-ld=lld on macOS. This caused 'invalid linker name' errors when running rust:check or debug builds. Only apply the LLD flag on non-Windows, non-Darwin platforms.
WalkthroughRefactors FFI callback handling to a threadsafe refcounted wrapper capturing execution context, adds a skipped threadsafe JSCallback test exercising pthread callbacks with interleaved GC, omits the lld linker flag for Darwin in the Rust build, fixes an internal module path UTF-8 construction, and replaces AGENTS.md content with a reference to CLAUDE.md. ChangesFFI Callback Thread-Safety
Build Flags and Internal Module Loading
Developer Documentation
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Jarred-Sumner
left a comment
There was a problem hiding this comment.
The fix looks real but can you undo the AGENTS.md change? Most of the coding products follow symlinks.
|
@Jarred-Sumner I have fulfilled your request. Sorry for the late correction. |
There was a problem hiding this comment.
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)
scripts/build/rust.ts (1)
484-491: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winUpdate comment to document Darwin exclusion.
The comment at lines 484-488 explains why Windows is excluded from the
-fuse-ld=lldflag, but the code now also excludes Darwin (macOS). The comment should be updated to document both exclusions for future maintainability.📝 Proposed comment update
// Not on Windows: the per-target linker there is `link.exe` / `lld-link.exe` // (see `CARGO_TARGET_*_LINKER` below), which take `/X` args, not the GCC/clang // `-fuse-ld=`. RUSTFLAGS only reach *target* crates when `--target` is given, // and the `bun_bin` staticlib has no link step, so it's normally dead — but // if a target cdylib ever appears it'd fail with "could not open '-fuse-ld=lld'". + // Not on Darwin: macOS uses the system ld64 linker by default, and Homebrew's + // clang++ (llvm@21+) rejects the `-fuse-ld=lld` flag. if (!cfg.windows && !cfg.darwin) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build/rust.ts` around lines 484 - 491, The comment explaining why we avoid adding `-fuse-ld=lld` only mentions Windows; update it to also document the Darwin exclusion by referencing the `cfg.windows` and `cfg.darwin` checks around the `rustflags.push(`-Clink-arg=-fuse-ld=lld`)` call so future readers understand both Windows and macOS are excluded (mentioning that macOS's linker/requirements differ and thus we skip adding `-fuse-ld=lld` on Darwin in the same way we skip it on Windows).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/jsc/bindings/JSFFIFunction.cpp`:
- Around line 56-61: The constructor FFICallbackFunctionWrapper currently
dereferences globalObject->scriptExecutionContext() when initializing
m_contextIdentifier; add a defensive null check (or assert) for
globalObject->scriptExecutionContext() before using it so we don't crash when it
is nullptr—e.g., in the FFICallbackFunctionWrapper(JSC::JSFunction* function,
Zig::GlobalObject* globalObject) initializer or body, verify
scriptExecutionContext() is non-null and only call identifier() when valid (or
set m_contextIdentifier to a safe default/invalid value and log/assert) to match
the null-checking pattern used elsewhere (see uses in JSAbortSignal.cpp,
JSCallbackData.cpp, JSPerformanceObserver.cpp).
---
Outside diff comments:
In `@scripts/build/rust.ts`:
- Around line 484-491: The comment explaining why we avoid adding `-fuse-ld=lld`
only mentions Windows; update it to also document the Darwin exclusion by
referencing the `cfg.windows` and `cfg.darwin` checks around the
`rustflags.push(`-Clink-arg=-fuse-ld=lld`)` call so future readers understand
both Windows and macOS are excluded (mentioning that macOS's linker/requirements
differ and thus we skip adding `-fuse-ld=lld` on Darwin in the same way we skip
it on Windows).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8985a2a1-65fc-4bbd-ab8b-a1e6cec7835f
📒 Files selected for processing (6)
AGENTS.mdAGENTS.mdscripts/build/rust.tssrc/jsc/bindings/InternalModuleRegistry.cppsrc/jsc/bindings/JSFFIFunction.cpptest/js/bun/ffi/cc.test.ts
| FFICallbackFunctionWrapper(JSC::JSFunction* function, Zig::GlobalObject* globalObject) | ||
| : m_function(globalObject->vm(), function) | ||
| , globalObject(globalObject->vm(), globalObject) | ||
| , m_contextIdentifier(globalObject->scriptExecutionContext()->identifier()) | ||
| { | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if scriptExecutionContext() can return nullptr in GlobalObject implementations
rg -nC3 'scriptExecutionContext\(\)' --type cpp | head -80Repository: oven-sh/bun
Length of output: 5768
🏁 Script executed:
# Find Zig::GlobalObject implementation and scriptExecutionContext() definition
fd -t f 'GlobalObject\|Zig' --type h --type cpp | head -30Repository: oven-sh/bun
Length of output: 224
🏁 Script executed:
# Search for Zig::GlobalObject class definition
rg -n 'class\s+GlobalObject' --type cpp --type h | head -20Repository: oven-sh/bun
Length of output: 1263
🏁 Script executed:
# Look for scriptExecutionContext() implementation in GlobalObject
rg -nB2 -A5 'GlobalObject.*scriptExecutionContext\(\)' --type cpp --type hRepository: oven-sh/bun
Length of output: 17952
🏁 Script executed:
# Check what calls FFICallbackFunctionWrapper constructor
rg -n 'FFICallbackFunctionWrapper' --type cpp | head -20Repository: oven-sh/bun
Length of output: 2756
Add null check for scriptExecutionContext() on line 59.
The constructor dereferences globalObject->scriptExecutionContext()->identifier() without null checking. The method returns a raw pointer that can be nullptr, as evidenced by the widespread null-checking pattern throughout the codebase (JSAbortSignal.cpp, JSCallbackData.cpp, JSPerformanceObserver.cpp, and others all defensively check this pointer). Add a null check or assert to prevent a potential crash during FFICallbackFunctionWrapper construction.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/jsc/bindings/JSFFIFunction.cpp` around lines 56 - 61, The constructor
FFICallbackFunctionWrapper currently dereferences
globalObject->scriptExecutionContext() when initializing m_contextIdentifier;
add a defensive null check (or assert) for
globalObject->scriptExecutionContext() before using it so we don't crash when it
is nullptr—e.g., in the FFICallbackFunctionWrapper(JSC::JSFunction* function,
Zig::GlobalObject* globalObject) initializer or body, verify
scriptExecutionContext() is non-null and only call identifier() when valid (or
set m_contextIdentifier to a safe default/invalid value and log/assert) to match
the null-checking pattern used elsewhere (see uses in JSAbortSignal.cpp,
JSCallbackData.cpp, JSPerformanceObserver.cpp).
What does this PR do?
Prevents the build system from forcing -fuse-ld=lld on macOS.
On macOS, Homebrew's llvm@21 clang++ does not support the -fuse-ld=lld flag, which caused the following error when running bun run rust:check or building debug binaries:
This change makes the LLD linker flag only apply to non-Windows, non-Darwin platforms.
How did you verify your code works?
Closes #30870