Skip to content

fix ENG-24015#25222

Merged
Jarred-Sumner merged 1 commit into
mainfrom
dylan/fix-ffi-ptr-to-uint64
Nov 30, 2025
Merged

fix ENG-24015#25222
Jarred-Sumner merged 1 commit into
mainfrom
dylan/fix-ffi-ptr-to-uint64

Conversation

@dylan-conway

@dylan-conway dylan-conway commented Nov 29, 2025

Copy link
Copy Markdown
Member

What does this PR do?

Ensures ptr is either a number or heap big int before converting to a number.

also fixes ENG-24039

How did you verify your code works?

Added a test

@linear

linear Bot commented Nov 29, 2025

Copy link
Copy Markdown

@robobun

robobun commented Nov 29, 2025

Copy link
Copy Markdown
Collaborator
Updated 8:35 AM PT - Nov 29th, 2025

@dylan-conway, your commit 11d1c4f has 3 failures in Build #32676 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 25222

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

bun-25222 --bun

@coderabbitai

coderabbitai Bot commented Nov 29, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds support for HeapBigInt pointer types in FFI symbol resolution, enabling symbol_from_dynamic_library assignment from big integer heap values. Documentation and declaration ordering updates are made to JSValue bindings, and a test case for invalid pointer error handling is added.

Changes

Cohort / File(s) Summary
FFI pointer handling
src/bun.js/api/ffi.zig
Adds HeapBigInt branch in generateSymbolForFunction to handle big-int pointers, converting them to 64-bit unsigned integers and assigning as symbol_from_dynamic_library when non-zero
JSValue bindings
src/bun.js/bindings/JSValue.zig
Adds documentation comment to toUInt64NoTruncate method specifying expected value types (isHeapBigInt or isNumber); reorders extern declaration
FFI error handling tests
test/js/bun/ffi/ffi-error-messages.test.ts
Adds test case "linkSymbols with non-number ptr does not crash" to verify error message for invalid pointer types in linkSymbols calls

Possibly related PRs

Suggested reviewers

  • taylordotfish
  • nektro

Pre-merge checks

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix ENG-24015' references a specific issue but lacks descriptive detail about what was actually fixed in the codebase. Consider using a more descriptive title like 'Fix FFI pointer type validation to prevent crash on non-number/BigInt values' to clearly communicate the change.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the fix required by ENG-24015: validating that ptr is either a number or heap BigInt before conversion, preventing the assertion failure that caused the crash.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the FFI pointer handling issue: validation logic in ffi.zig, documentation in JSValue.zig, and a corresponding test.
Description check ✅ Passed The pull request description covers both required sections with clear, concise information about what the PR does and how it was verified.

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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/bun.js/api/ffi.zig (1)

1358-1369: Consider explicit validation for invalid ptr types.

The HeapBigInt handling correctly prevents the crash described in the linked issue. However, when ptr is provided but is neither a number nor a HeapBigInt (e.g., a string), the code silently ignores it, leaving symbol_from_dynamic_library as null. The downstream error at line 1190 reports the symbol is "missing" a ptr field, which could confuse users since ptr was provided—it's just the wrong type.

Consider adding explicit type validation to provide a clearer error message:

 if (try value.get(global, "ptr")) |ptr| {
-    if (ptr.isNumber()) {
+    if (!ptr.isNumber() and !ptr.isHeapBigInt()) {
+        abi_types.clearAndFree(allocator);
+        return global.toInvalidArguments("ptr must be a number or BigInt, got {s}", .{@tagName(ptr.jsTypeLoose())});
+    }
+    if (ptr.isNumber()) {
         const num = ptr.asPtrAddress();
         if (num > 0)
             function.symbol_from_dynamic_library = @as(*anyopaque, @ptrFromInt(num));
     } else if (ptr.isHeapBigInt()) {
         const num = ptr.toUInt64NoTruncate();
         if (num > 0) {
             function.symbol_from_dynamic_library = @as(*anyopaque, @ptrFromInt(num));
         }
     }
 }

This would fail immediately with a clear message like "ptr must be a number or BigInt, got string" rather than later reporting ptr is "missing".

📜 Review details

Configuration used: Path: .coderabbit.yaml

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 0f74945 and 11d1c4f.

📒 Files selected for processing (3)
  • src/bun.js/api/ffi.zig (1 hunks)
  • src/bun.js/bindings/JSValue.zig (1 hunks)
  • test/js/bun/ffi/ffi-error-messages.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
test/**/*.{js,ts,jsx,tsx}

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

test/**/*.{js,ts,jsx,tsx}: Write tests as JavaScript and TypeScript files using Jest-style APIs (test, describe, expect) and import from bun:test
Use test.each and data-driven tests to reduce boilerplate when testing multiple similar cases

Files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
test/js/bun/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

For Bun-specific API tests, use the test/js/bun/ directory (for http, crypto, ffi, shell, etc.)

Files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: For single-file tests, prefer using -e flag over tempDir
For multi-file tests, prefer using tempDir from harness and Bun.spawn over other temporary directory creation methods
Always use port: 0 for network tests and do not hardcode ports or use custom random port number functions
Use normalizeBunSnapshot to normalize snapshot output of tests
Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test
Use tempDir from harness to create temporary directories, do not use tmpdirSync or fs.mkdtempSync
When spawning processes in tests, check stdout/stderr expectations BEFORE checking exit code to get more useful error messages on test failure
Do not write flaky tests - do not use setTimeout in tests, instead await the condition to be met as you are testing the CONDITION not the TIME PASSING
Verify your test fails with USE_SYSTEM_BUN=1 bun test <file> and passes with bun bd test <file> - your test is NOT VALID if it passes with USE_SYSTEM_BUN=1

Files:

  • test/js/bun/ffi/ffi-error-messages.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 with files that end in *.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always use port: 0 to get a random port
Prefer concurrent tests over sequential tests using test.concurrent or describe.concurrent when multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, use bunExe and bunEnv from harness to ensure the same build of Bun is used and debug logging is silenced
Use -e flag for single-file tests when spawning Bun processes
Use tempDir() from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, use Promise.withResolvers to create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
Use Buffer.alloc(count, fill).toString() instead of 'A'.repeat(count) to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Use describe blocks for grouping related tests
Always use await using or using to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Use describe.each() for parameterized tests
Use toMatchSnapshot() for snapshot testing
Use beforeAll(), afterEach(), beforeEach() for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup in afterEach()

Files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
src/**/*.{cpp,zig}

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

src/**/*.{cpp,zig}: Use bun bd or bun run build:debug to build debug versions for C++ and Zig source files; creates debug build at ./build/debug/bun-debug
Run tests using bun bd test <test-file> with the debug build; never use bun test directly as it will not include your changes
Execute files using bun bd <file> <...args>; never use bun <file> directly as it will not include your changes
Enable debug logs for specific scopes using BUN_DEBUG_$(SCOPE)=1 environment variable
Code generation happens automatically as part of the build process; no manual code generation commands are required

Files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
src/**/*.zig

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

Use bun.Output.scoped(.${SCOPE}, .hidden) for creating debug logs in Zig code

Implement core functionality in Zig, typically in its own directory in src/

src/**/*.zig: Zig source code should be organized in src/*.zig for core runtime, JavaScript bindings, and package manager
In Zig code, be careful with allocators and use defer for cleanup to manage memory properly
Use BUN_DEBUG_QUIET_LOGS=1 to disable debug logging, or BUN_DEBUG_<scopeName>=1 to enable specific Output.scoped calls in debug builds
Run bun run zig:check-all to compile the Zig code on all platforms when making platform-specific changes

src/**/*.zig: Private fields in Zig are fully supported using the # prefix: struct { #foo: u32 };
Use decl literals in Zig for declaration initialization: const decl: Decl = .{ .binding = 0, .value = 0 };
Prefer @import at the bottom of the file (auto formatter will move them automatically)

Files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
**/*.zig

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

**/*.zig: Expose generated bindings in Zig structs using pub const js = JSC.Codegen.JS<ClassName> with trait conversion methods: toJS, fromJS, and fromJSDirect
Use consistent parameter name globalObject instead of ctx in Zig constructor and method implementations
Use bun.JSError!JSValue return type for Zig methods and constructors to enable proper error handling and exception propagation
Implement resource cleanup using deinit() method that releases resources, followed by finalize() called by the GC that invokes deinit() and frees the pointer
Use JSC.markBinding(@src()) in finalize methods for debugging purposes before calling deinit()
For methods returning cached properties in Zig, declare external C++ functions using extern fn and callconv(JSC.conv) calling convention
Implement getter functions with naming pattern get<PropertyName> in Zig that accept this and globalObject parameters and return JSC.JSValue
Access JavaScript CallFrame arguments using callFrame.argument(i), check argument count with callFrame.argumentCount(), and get this with callFrame.thisValue()
For reference-counted objects, use .deref() in finalize instead of destroy() to release references to other JS objects

Files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
src/**/*.{ts,zig,cpp}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute paths in file operations

Files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
src/**/*.{ts,zig}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid shell commands in code - don't use find or grep; use Bun's Glob and built-in tools instead

Files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
🧠 Learnings (35)
📓 Common learnings
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 22946
File: test/js/sql/sql.test.ts:195-202
Timestamp: 2025-09-25T22:07:13.851Z
Learning: PR oven-sh/bun#22946: JSON/JSONB result parsing updates (e.g., returning parsed arrays instead of legacy strings) are out of scope for this PR; tests keep current expectations with a TODO. Handle parsing fixes in a separate PR.
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: src/bun.js/telemetry.zig:458-475
Timestamp: 2025-10-18T20:59:45.579Z
Learning: In src/bun.js/telemetry.zig, the RequestId (u64) to JavaScript number (f64) conversion in jsRequestId() is intentionally allowed to lose precision beyond 2^53-1. This is acceptable because: (1) at 1M requests/sec it takes ~285 years to overflow, (2) the counter resets per-process, and (3) these are observability IDs, not critical distributed IDs. Precision loss is an acceptable trade-off for this use case.
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 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.
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Use tagged pointers for efficient V8 value representation where small integers are stored directly as Smis and objects use pointer tagging with map pointers
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/src/symbols.txt : Add symbol names without leading underscore to src/symbols.txt for each new V8 API method

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
  • src/bun.js/api/ffi.zig
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.ts : Builtin functions must include `this` parameter typing in TypeScript to enable direct method binding in C++

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8-module/main.cpp : Create test functions in test/v8/v8-module/main.cpp that take FunctionCallbackInfo<Value> parameter, use the test V8 API, print results for comparison with Node.js, and return Undefined

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : For Bun-specific API tests, use the `test/js/bun/` directory (for http, crypto, ffi, shell, etc.)

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>` - your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/**/*.test.ts : Specify expected bundling errors via the `errors` option when a client is expected to encounter an error; by default, a client opening a page to an error fails the test

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Use `Buffer.alloc(count, fill).toString()` instead of `'A'.repeat(count)` to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-10-18T23:43:42.502Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 23817
File: src/js/node/test.ts:282-282
Timestamp: 2025-10-18T23:43:42.502Z
Learning: In the Bun repository, the error code generation script (generate-node-errors.ts) always runs during the build process. When reviewing code that uses error code intrinsics like $ERR_TEST_FAILURE, $ERR_INVALID_ARG_TYPE, etc., do not ask to verify whether the generation script has been run or will run, as it is automatically executed.

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/src/symbols.dyn : Add symbol names with leading underscore and semicolons in braces to src/symbols.dyn for each new V8 API method

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
  • src/bun.js/api/ffi.zig
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: See `test/harness.ts` for common test utilities and helpers

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to **/*.test.{ts,tsx} : Never write tests that check for no 'panic' or 'uncaught exception' or similar in the test output - that is NOT a valid test

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-10-08T13:48:02.430Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 23373
File: test/js/bun/tarball/extract.test.ts:107-111
Timestamp: 2025-10-08T13:48:02.430Z
Learning: In Bun's test runner, use `expect(async () => { await ... }).toThrow()` to assert async rejections. Unlike Jest/Vitest, Bun does not require `await expect(...).rejects.toThrow()` - the async function wrapper with `.toThrow()` is the correct pattern for async error assertions in Bun tests.

Applied to files:

  • test/js/bun/ffi/ffi-error-messages.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/src/napi/napi.zig : For each new V8 C++ method, add both GCC/Clang and MSVC mangled symbol names to the V8API struct in src/napi/napi.zig using extern fn declarations

Applied to files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/bun.js/api/BunObject.zig : Implement getter functions in `src/bun.js/api/BunObject.zig` that return your feature, and export them in the `exportAll()` function

Applied to files:

  • src/bun.js/api/ffi.zig
📚 Learning: 2025-10-15T20:19:37.256Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23680
File: cmake/targets/BuildBun.cmake:822-822
Timestamp: 2025-10-15T20:19:37.256Z
Learning: In the Bun codebase, FFI (Foreign Function Interface) code is compiled separately using TinyCC (tcc), which barely supports C99. Headers like src/bun.js/api/FFI.h and src/bun.js/api/ffi-stdbool.h are only used for FFI compilation with tcc, not with the main compiler. Therefore, C standard changes to the main Bun target do not affect FFI code compilation.

Applied to files:

  • src/bun.js/api/ffi.zig
📚 Learning: 2025-11-24T18:36:33.069Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:33.069Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : When implementing JavaScript classes in C++, define properties using HashTableValue arrays and add iso subspaces for classes with C++ fields, caching structures in ZigGlobalObject

Applied to files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to src/bun.js/bindings/generated_classes_list.zig : Include new class bindings in `src/bun.js/bindings/generated_classes_list.zig` to register them with the code generator

Applied to files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-09-02T18:32:32.309Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 22227
File: src/bun.zig:668-675
Timestamp: 2025-09-02T18:32:32.309Z
Learning: The `std.meta.trait` module has been removed from the Zig standard library, which will cause compilation failures in functions that use it like `std.meta.trait.isSingleItemPtr()`. This affects functions like `isHeapMemory()` in `src/bun.zig`.

Applied to files:

  • src/bun.js/api/ffi.zig
📚 Learning: 2025-11-24T18:35:25.883Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-11-24T18:35:25.883Z
Learning: Applies to *.zig : In Zig, declare external C++ functions and wrap them in public methods using the convention `extern fn Bun__ClassName__toJS(...)` and `pub fn toJS(...)`

Applied to files:

  • src/bun.js/api/ffi.zig
  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to **/js_*.zig : Implement proper memory management with reference counting using `ref()`/`deref()` in JavaScript bindings

Applied to files:

  • src/bun.js/api/ffi.zig
📚 Learning: 2025-10-01T21:59:54.571Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 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/JSValue.zig
📚 Learning: 2025-10-18T20:50:47.750Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: src/bun.js/telemetry.zig:366-373
Timestamp: 2025-10-18T20:50:47.750Z
Learning: In Bun's Zig codebase (src/bun.js/bindings/JSValue.zig), the JSValue enum uses `.null` (not `.js_null`) for JavaScript's null value. Only `js_undefined` has the `js_` prefix to avoid collision with Zig's built-in `undefined` keyword. The correct enum fields are: `js_undefined`, `null`, `true`, `false`, and `zero`.

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to **/js_*.zig : Use `bun.JSError!JSValue` for proper error propagation in JavaScript bindings

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Use `bun.JSError!JSValue` return type for Zig methods and constructors to enable proper error handling and exception propagation

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Expose generated bindings in Zig structs using `pub const js = JSC.Codegen.JS<ClassName>` with trait conversion methods: `toJS`, `fromJS`, and `fromJSDirect`

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to **/*js_bindings.classes.ts : Use `JSC.Codegen` correctly to generate necessary binding code for JavaScript-Zig integration

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-09-07T14:00:36.526Z
Learnt from: RiskyMH
Repo: oven-sh/bun PR: 22258
File: src/bun.js/test/jest.zig:63-86
Timestamp: 2025-09-07T14:00:36.526Z
Learning: In Bun's Zig codebase, the established pattern for HashMap hash functions is to truncate hash values to u32, even when the underlying hash computation produces u64. This is seen throughout the codebase, such as in src/css/rules/rules.zig where u64 hashes are stored but truncated to u32 in HashMap contexts. When implementing HashContext for HashMapUnmanaged, the hash function should return u32 to match this pattern.

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-12T04:11:52.293Z
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 24622
File: src/deps/uws/us_socket_t.zig:112-113
Timestamp: 2025-11-12T04:11:52.293Z
Learning: In Bun's Zig codebase, when passing u32 values to C FFI functions that expect c_uint parameters, no explicit intCast is needed because c_uint is equivalent to u32 on Bun's target platforms and Zig allows implicit coercion between equivalent types. This pattern is used consistently throughout src/deps/uws/us_socket_t.zig in functions like setTimeout, setLongTimeout, and setKeepalive.

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-10-18T20:59:45.579Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: src/bun.js/telemetry.zig:458-475
Timestamp: 2025-10-18T20:59:45.579Z
Learning: In src/bun.js/telemetry.zig, the RequestId (u64) to JavaScript number (f64) conversion in jsRequestId() is intentionally allowed to lose precision beyond 2^53-1. This is acceptable because: (1) at 1M requests/sec it takes ~285 years to overflow, (2) the counter resets per-process, and (3) these are observability IDs, not critical distributed IDs. Precision loss is an acceptable trade-off for this use case.

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Implement getter functions with naming pattern `get<PropertyName>` in Zig that accept `this` and `globalObject` parameters and return `JSC.JSValue`

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-10-24T10:43:09.398Z
Learnt from: fmguerreiro
Repo: oven-sh/bun PR: 23774
File: src/install/PackageManager/updatePackageJSONAndInstall.zig:548-548
Timestamp: 2025-10-24T10:43:09.398Z
Learning: In Bun's Zig codebase, the `as(usize, intCast(...))` cast pattern triggers a Zig compiler bug that causes compilation to hang indefinitely when used in complex control flow contexts (loops + short-circuit operators + optional unwrapping). Avoid this pattern and use simpler alternatives like just `intCast(...)` if type casting is necessary.

Applied to files:

  • src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-11-24T18:35:25.883Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-11-24T18:35:25.883Z
Learning: Applies to *.cpp : To create JavaScript objects from Zig, implement C++ functions following the `Bun__ClassName__toJS(Zig::GlobalObject*, NativeType*)` convention that construct and return the JavaScript object as an encoded JSValue

Applied to files:

  • src/bun.js/bindings/JSValue.zig
🔇 Additional comments (1)
src/bun.js/bindings/JSValue.zig (1)

1159-1163: LGTM! Documentation clarifies the precondition.

The doc comment accurately describes the expected types for toUInt64NoTruncate, matching the assertion in the linked issue. Reordering the extern declaration improves code organization.

Comment on lines +66 to +75
test("linkSymbols with non-number ptr does not crash", () => {
expect(() => {
linkSymbols({
fn: {
// @ts-expect-error
ptr: "not a number",
},
});
}).toThrow('you must provide a "ptr" field with the memory address of the native function.');
});

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the test file to see the actual implementation
cat -n test/js/bun/ffi/ffi-error-messages.test.ts | head -100

Repository: oven-sh/bun

Length of output: 3146


🏁 Script executed:

#!/bin/bash
# Search for linkSymbols implementation
rg -n "linkSymbols" --type=ts --type=tsx -A 10 -B 2 | head -150

Repository: oven-sh/bun

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Search for linkSymbols implementation
rg -n "linkSymbols" -A 10 -B 2 | head -200

Repository: oven-sh/bun

Length of output: 12556


🏁 Script executed:

#!/bin/bash
# Check FFIFunction type definition
rg -n "type FFIFunction\|interface FFIFunction" -A 20 | head -100

Repository: oven-sh/bun

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Look at the FFI type definitions
cat packages/bun-types/ffi.d.ts | head -300

Repository: oven-sh/bun

Length of output: 5483


🏁 Script executed:

#!/bin/bash
# Search for FFIFunction interface/type
grep -n "FFIFunction\|interface.*Function\|type.*Function" packages/bun-types/ffi.d.ts | head -30

Repository: oven-sh/bun

Length of output: 1239


🏁 Script executed:

#!/bin/bash
# Read FFIFunction interface definition
sed -n '453,540p' packages/bun-types/ffi.d.ts

Repository: oven-sh/bun

Length of output: 2503


🏁 Script executed:

#!/bin/bash
# Search for Pointer type definition
rg -n "type Pointer\|interface Pointer" packages/bun-types/ffi.d.ts -A 5

Repository: oven-sh/bun

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Search for Pointer definition
grep -n "Pointer" packages/bun-types/ffi.d.ts | head -20

Repository: oven-sh/bun

Length of output: 1266


🏁 Script executed:

#!/bin/bash
# Search for ptr handling in Zig FFI code
rg -n "symbol_from_dynamic_library\|ptr" src/bun.js/api/ffi.zig -B 3 -A 3 | head -200

Repository: oven-sh/bun

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Read the linkSymbols and generateSymbols functions
sed -n '1000,1250p' src/bun.js/api/ffi.zig

Repository: oven-sh/bun

Length of output: 10775


🏁 Script executed:

#!/bin/bash
# Search for generateSymbols function
rg -n "fn generateSymbols" src/bun.js/api/ffi.zig -A 100 | head -200

Repository: oven-sh/bun

Length of output: 4161


🏁 Script executed:

#!/bin/bash
# Search for generateSymbolForFunction
rg -n "fn generateSymbolForFunction" src/bun.js/api/ffi.zig -A 150 | head -250

Repository: oven-sh/bun

Length of output: 6920


🏁 Script executed:

#!/bin/bash
# Verify the error message text matches between test and Zig code
echo "=== From test (line 74) ==="
sed -n '74p' test/js/bun/ffi/ffi-error-messages.test.ts

echo ""
echo "=== From Zig code (line 1190) ==="
sed -n '1190p' src/bun.js/api/ffi.zig

Repository: oven-sh/bun

Length of output: 473


🏁 Script executed:

#!/bin/bash
# Check test file structure and imports
head -10 test/js/bun/ffi/ffi-error-messages.test.ts
echo ""
echo "=== Test count ==="
grep -c "test(" test/js/bun/ffi/ffi-error-messages.test.ts

Repository: oven-sh/bun

Length of output: 431


🏁 Script executed:

#!/bin/bash
# Summary of findings
cat > /tmp/summary.txt << 'EOF'
VERIFICATION RESULTS:

1. TEST IMPLEMENTATION
   - File: test/js/bun/ffi/ffi-error-messages.test.ts, lines 66-75
   - Uses correct test pattern: expect(() => {...}).toThrow()
   - Error message substring matches the full error from Zig code

2. FFI IMPLEMENTATION (src/bun.js/api/ffi.zig)
   - Lines 1358-1369: ptr field extraction
   - Accepts: number (line 1359) or HeapBigInt (line 1363)
   - String values: Not explicitly rejected, but result in null symbol_from_dynamic_library
   - LinkSymbols checks null and throws expected error (line 1190)

3. TYPE DEFINITIONS (packages/bun-types/ffi.d.ts)
   - Line 521: FFIFunction.ptr type is "Pointer | bigint"
   - Pointer = number with phantom type
   - So ptr accepts: number or bigint

4. REVIEW COMMENT ANALYSIS
   Original claims:
   a) "Test correctly validates error handling for non-numeric ptr values" - TRUE
   b) "Verify test fails with USE_SYSTEM_BUN=1 and passes with bun bd test" - RECOMMENDATION (valid)
   c) "Consider adding test for valid HeapBigInt ptr values" - VALID SUGGESTION
      (Types support bigint, implementation supports bigint, but no test validates it)

5. CODING GUIDELINE COMPLIANCE
   - ✓ Uses bun:test correctly
   - ✓ Appropriate test pattern for error validation
   - ✓ No flaky patterns or resource issues
   - ✓ In correct FFI test directory
EOF
cat /tmp/summary.txt

Repository: oven-sh/bun

Length of output: 1386


Verify test follows coding guidelines for Bun-specific FFI tests and add validation for supported BigInt ptr values.

The test correctly validates that string ptr values throw the expected error. However, there are two improvements needed:

  1. Verify test behavior pattern: Per coding guidelines, you must verify the test fails with USE_SYSTEM_BUN=1 bun test test/js/bun/ffi/ffi-error-messages.test.ts and passes with bun bd test test/js/bun/ffi/ffi-error-messages.test.ts to ensure it's testing Bun's canary build behavior, not system Bun.

  2. Add test for valid BigInt ptr values: The FFI implementation (src/bun.js/api/ffi.zig, lines 1363-1367) and type definitions (packages/bun-types/ffi.d.ts, line 521) both explicitly support bigint for the ptr field. Currently, the test only validates rejection of invalid types but doesn't verify that valid BigInt pointers are accepted. Add:

test("linkSymbols accepts BigInt ptr", () => {
  const lib = linkSymbols({
    fn: {
      ptr: 0x123456789ABCDEFn,
      args: [],
      returns: "void",
    },
  });
  expect(lib.symbols.fn).toBeDefined();
});

@linear

linear Bot commented Nov 29, 2025

Copy link
Copy Markdown

@Jarred-Sumner Jarred-Sumner merged commit cc3fc5a into main Nov 30, 2025
58 of 60 checks passed
@Jarred-Sumner Jarred-Sumner deleted the dylan/fix-ffi-ptr-to-uint64 branch November 30, 2025 03:13
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