Skip to content

bun:ffi: infer exact signatures from symbol definitions, fix cc argument/return conversions - #32075

Closed
robobun wants to merge 7 commits into
mainfrom
farm/b202b1d9/ffi-type-safety
Closed

bun:ffi: infer exact signatures from symbol definitions, fix cc argument/return conversions#32075
robobun wants to merge 7 commits into
mainfrom
farm/b202b1d9/ffi-type-safety

Conversation

@robobun

@robobun robobun commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

What

Makes bun:ffi's TypeScript surface infer exact call signatures from symbol definitions, and fixes the runtime bugs that surfaced while verifying the types against actual behavior.

Types (packages/bun-types/ffi.d.ts)

  • dlopen, cc, linkSymbols, CFunction, and JSCallback now use const type parameters. Argument tuples infer exactly without as const, so arity is checked:

    const lib = dlopen("libadd.so", {
      add: { args: ["i32", "i32"], returns: "i32" },
    });
    lib.symbols.add(1, 2);       // number
    lib.symbols.add(1, 2, 3);    // error: Expected 2 arguments, but got 3

    Previously argument types were checked but arity was not (Parameters collapsed to number[] unless the caller wrote as const).

  • CFunction returns a typed call signature derived from the definition instead of CallableFunction.

  • JSCallback infers the callback's parameters and return type from the definition. The mappings follow the native trampoline: cstring arguments arrive as a raw Pointer | null (not CString), i64/u64 arrive as bigint. The callback parameter uses a bivariant method signature so narrower handwritten annotations like (ptr: Pointer) => void stay assignable.

  • ptr/cstring arguments accept ArrayBuffer and DataView, which the runtime converts.

  • FFITypeStringToType now matches the runtime string map: "function", "callback", and the previously missing "fn" resolve to FFIType.function (so string-spelled function arguments accept a JSCallback, like the enum spelling), and the other spellings the runtime accepts ("c_int", "c_uint", "isize", "char*", "void*", "i64_fast", "u64_fast") are representable.

Runtime (src/js/bun/ffi.ts)

  1. cc() never applied argument/return conversions. The wrapper-application loop read options[key], but cc nests definitions under options.symbols[key] (unlike dlopen(path, symbols)). Verified on 1.4.0-canary:

    • returns: "cstring" produced a raw pointer number instead of a CString
    • identity_u32(0xFFFFFFFF) returned 4292870144 (the JSValue encoding bug from FFIType.u32 misbehavior #7007 that the val|0 wrapper exists to prevent)
    • passing a JSCallback object as a "function" argument jumped to a garbage pointer: panic(main thread): Segmentation fault at address 0xFFFFFFFFFFFFFFFF

    This is also why the // FIXME: bus error skips in cc.test.ts existed: the skipped cstring tests crashed because the pointer-argument wrapper never ran.

  2. Pointer arguments now accept CString, unwrapping to its ptr. The published types have always claimed FFIType.ptr accepts CString, but the wrapper threw TypeError: Unable to convert hello to a pointer.

  3. DataView is accepted wherever TypedArrays are. The JS-side check used $isTypedArrayView, which excludes DataView, but the compiled stubs accept it (the JSType range check in FFI.h spans through DataView, and JSVALUE_TO_TYPED_ARRAY_VECTOR reads the ArrayBufferView vector).

  4. napi_env/napi_value arguments pass through unmodified. With cc now applying wrappers, the default val|0 coercion would corrupt napi_value JSValues (the native stub reads them raw via .asNapiValue). This also fixes the same latent bug for dlopen'd symbols with napi argument types.

Also fixed makeValidCase in cc.test.ts, which returned the library variable before beforeAll assigned it (always undefined), unnoticed because every test using it was skipped.

Tests

  • test/js/bun/ffi/ffi.test.js: new pointer argument conversion suite over libc strlen (CString, ArrayBuffer, DataView for ptr and buffer args, rejection of unconvertible values). Runs on Linux glibc, macOS, and Windows (msvcrt.dll), including ASAN builds.
  • test/js/bun/ffi/cc.test.ts: un-skipped the ping/strlen cstring suites and added cc applies the same conversions as dlopen (cstring returns, u32, ArrayBuffer, JSCallback function args, napi_value passthrough). These compile-success paths run under ASAN; the TinyCC setjmp conflict only affects its compile-error handling.
  • test/integration/bun-types/fixture/ffi.ts: type-level assertions for inferred arity, CFunction, and JSCallback signatures, with @ts-expect-error directives that are unused against the old types.

All fail on the unfixed build: the fixture produces 6 type-check diagnostics, ffi.test.js fails 3 tests, and cc.test.ts segfaults where the pointer wrapper is missing. bun-types integration test (tsc + tsgo, DOM and no-DOM) passes.

Docs: updated the JSCallback examples in docs/runtime/ffi.mdx to be type-correct under the stricter signatures (they also passed length as CString's byteOffset parameter; now new CString(ptr, 0, Number(length))).

Overlap with other open PRs

The cc() definition-lookup bug has been independently found and fixed in three other open PRs. Map of the overlap so this is easy to triage:

The overlapping hunks are semantically identical in all of them, so whichever lands first, the others rebase trivially. What only this PR contains: the type-level inference work (const type parameters, typed CFunction/JSCallback), DataView acceptance, the conversion test suites that run under ASAN, and the makeValidCase fixture repair.

Fixes #235
Fixes #24518

@robobun

robobun commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 5:07 PM PT - Jun 28th, 2026

@robobun, your commit 58fd5a7 has 1 failures in Build #66503 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 32075

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

bun-32075 --bun

@mintlify

mintlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
bun 🟢 Ready View Preview Jun 10, 2026, 8:59 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@github-actions

Copy link
Copy Markdown
Contributor

Found 3 issues this PR may fix:

  1. FFI Type Inference #235 - PR adds const type parameters to dlopen, cc, linkSymbols, CFunction, and JSCallback for automatic argument arity/type inference without as const
  2. Cannot pass CString to FFIType.cstring #24518 - PR adds CString .ptr unwrapping when passed as a pointer argument, directly fixing the TypeError: Unable to convert ... to a pointer reported here
  3. ffi: napi_env can only be the last argument of the foreign function being called #29517 - PR adds napi_env/napi_value passthrough wrappers that pass values unmodified instead of val|0 coercion, changing how napi_env arguments are processed

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #235
Fixes #24518
Fixes #29517

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. fix(ffi): multiple long-standing FFI correctness bugs #31449 - Fixes the same cc() options[key]options.symbols[key] bug, CString-as-pointer passthrough, and napi_env/napi_value wrappers
  2. ffi: wrap JSCallback objects passed as cc() function arguments #31776 - Fixes the same cc() options[key]options.symbols[key] wrapper lookup bug
  3. docs(ffi): cover cc, viewSource, Node-API types, and fix CFunction usage #31535 - Fixes the same cc() wrapper lookup bug and napi_env/napi_value passthrough, alongside documentation updates

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Improves bun:ffi by broadening accepted pointer input types to include DataView and ArrayBuffer, fixing CString passthrough for cstring-typed parameters, correcting cc symbol config lookup, and adding strongly-typed generics for JSCallback, CFunction, dlopen, cc, and linkSymbols. Docs and tests are updated to match.

FFI typing, coercion, and verification

Layer / File(s) Summary
Type contract refactor for callbacks and function inference
packages/bun-types/ffi.d.ts
Expands ptr and cstring arg unions to include DataView and ArrayBuffer; adds FFITypeToJSCallbackArgsType, FFITypeToJSCallbackReturnsType, ConvertFn, and JSCallbackFunction; applies const generics to dlopen, cc, and linkSymbols; makes JSCallback and CFunction definition-derived; updates FFITypeStringToType for "function"/"callback"/"fn" and integer aliases.
Runtime pointer conversion and wrapper updates
src/js/bun/ffi.ts
Accepts any ArrayBuffer view (including DataView) as a pointer input; extracts .ptr from CString instances in cstring/pointer wrappers; updates buffer error text; removes coercion for napi_env/napi_value; fixes cc symbol config to read from options.symbols[key]; extends FFIType with string keys "18"/"19"/"20" and size_t alias.
JSCallback documentation sync
docs/runtime/ffi.mdx
Updates callback examples to import type Pointer, annotate JSCallback params as ptr: Pointer and length: bigint, and construct CString with explicit offset and byte length.
Type and runtime validation coverage
test/integration/bun-types/fixture/ffi.ts, test/js/bun/ffi/cc.test.ts, test/js/bun/ffi/ffi.test.js
Adds type-fixture assertions for dlopen/cc/CFunction/JSCallback inference including cstring-as-Pointer, arity errors, and string-spelling equivalence; updates cc tests for CString pointer comparison, bigint strlen, numeric-string coercion, and holder-based teardown; adds dlopen strlen tests covering CString, ArrayBuffer, DataView, and rejection for string/plain-object inputs.

Suggested reviewers

  • Jarred-Sumner
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed It clearly summarizes the main FFI signature inference and cc conversion fixes.
Description check ✅ Passed It includes the required change summary and concrete verification notes, though the headings differ from the template.
Linked Issues check ✅ Passed The PR implements exact type inference and accepts CString for pointer/cstring args, satisfying [#235, #24518].
Out of Scope Changes check ✅ Passed The extra type, runtime, test, and doc changes are all supporting the FFI inference and CString fixes, not unrelated scope.

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

@robobun

robobun commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

CI status: the diff's own tests are green on all lanes. The three red lanes across the last two runs are unrelated flakes, each from a different area and none touching bun:ffi:

  • build 61808: test/bake/dev-and-prod.test.ts (Windows x64, dev server, passed on retry elsewhere) and test-http-should-emit-close-when-connection-is-aborted.ts timeout (Windows aarch64)
  • build 61815: the same http abort timeout (Windows x64-baseline, 2 retries), a @prisma/engines postinstall exit 9 during test/package.json install (Windows aarch64, infra), and streams-leak.test.ts (aarch64 shard)

The one real CI failure this PR caused (ffi.test.js running on Windows ARM64 where bun:ffi is disabled) was fixed in 95a0ece and has stayed green since. Ready for review.

@alii

alii commented Jun 11, 2026

Copy link
Copy Markdown
Member

@robobun why did any test files change (except bun-types fixture, of course). help me understand please

@robobun

robobun commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

The PR is not types-only: src/js/bun/ffi.ts has four behavior fixes, and the test file changes are the coverage for those. The mapping:

ffi.test.js, new pointer argument conversion describe, covers the two pointer-wrapper changes:

  • CString passed as a ptr/cstring argument now unwraps to its .ptr. On main this throws TypeError: Unable to convert hello to a pointer even though FFITypeToArgsType has claimed CString support for years (this is issue Cannot pass CString to FFIType.cstring #24518).
  • The ArrayBufferView pre-check now accepts DataView, matching the native stub (the JSType range check in FFI.h includes DataView). On main, DataView throws for both ptr and buffer args while the published types claim it works for buffer.

cc.test.ts covers the cc() fix (symbol definitions read from options.symbols[key] instead of options[key], so FFIBuilder wrappers actually get applied):

  • New cc applies the same conversions as dlopen describe: cstring returns come back as CString instead of a raw pointer number, u32 args survive (identity_u32(0xFFFFFFFF) returns 4292870144 on main), a JSCallback object passed as a function arg no longer segfaults, and napi_value args pass through uncoerced (load-bearing because applying wrappers to cc would otherwise route napi args through the default val|0 coercion).
  • Un-skipped ping(cstr) and strlen(cstring) describes: their describe.skip / // FIXME: bus error comments were symptoms of this exact bug (CString went to native unconverted, producing a wild pointer read). The fix is what makes them passable, so un-skipping them is the acceptance criterion.
  • makeValidCase returned the library variable before beforeAll assigned it, so it always returned undefined. Nobody noticed because every test using it was skipped. Un-skipping required fixing it (holder object).
  • The skipped when passed arguments with incorrect types, throws test expected add("1", "2") to throw. With wrappers applied, int args go through the same val|0 coercion as dlopen, so the correct expectation is coercion (toBe(3)), and the "throws on unconvertible input" behavior is asserted where it actually exists, on pointer args.

The one commit after that (95a0ecec) gates the new ffi.test.js describe off Windows ARM64, where bun:ffi is disabled entirely.

Fail-before numbers for the whole set are in the PR description (ffi.test.js fails 3, cc.test.ts segfaults, fixture produces 6 diagnostics on old types).

@alii

alii commented Jun 11, 2026

Copy link
Copy Markdown
Member

Can you break it into two prs so i can quickly merge the typescript types? Or is it wrong to do that if the types rely on the behavioural changes

@alii

alii commented Jun 11, 2026

Copy link
Copy Markdown
Member

@robobun ^

@robobun
robobun force-pushed the farm/b202b1d9/ffi-type-safety branch from 132c867 to 3a717c4 Compare June 28, 2026 10:51
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main. The only conflict was the harness import line in test/js/bun/ffi/ffi.test.js (main gained imports from the NaN-purification and JSCallback-termination test additions, this branch added isMusl/isArm64/isWindows); resolved as the union. No semantic overlap with the upstream FFI changes: src/js/bun/ffi.ts on main only swapped $newZigFunction for $newRustFunction. Both test files pass locally after the rebase, including the new upstream tests.

@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

The bun-plugin-svelte check on this PR is failing on main too (same assertion on the __commonJS wrapper text: https://github.com/oven-sh/bun/actions/runs/28314465661/job/83884987730), so it's pre-existing and unrelated to this diff.

Comment thread docs/runtime/ffi.mdx Outdated
Comment thread test/js/bun/ffi/cc.test.ts Outdated
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the review comments in b9eb46c:

  • FFITypeStringToType["function"] and ["callback"] now map to FFIType.function (matching the runtime and native parsers), and the missing runtime-accepted spellings ("fn", "c_int", "c_uint", "isize", "char*", "void*", "i64_fast", "u64_fast") were added, with fixture assertions that fail against the old map.
  • Trimmed the oversized comment in cc.test.ts.
  • Dropped the inert non-null assertion in docs/runtime/ffi.mdx.

The CI failure on the previous run (build 66365) was a single unrelated flake (test/bake/deinitialization.test.ts on Windows aarch64).

@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

The "TypeScript types" check failure on the previous two runs was not a type error: every tsc lane passed, and the tsgo (TypeScript 7 preview) subtest failed to even start because @typescript/native-preview 7.0.0-dev.20260628.1 (published today) renamed its entry point from bin/tsgo.js to bin/tsgo, which test/integration/bun-types/bun-types.test.ts hardcoded. 18961a2 resolves the path from the package's bin field instead; the tsgo subtest passes locally again.

This workflow only runs for PRs that touch packages/bun-types, so any bun-types PR would have hit it starting today.

Comment thread src/js/bun/ffi.ts
Comment thread packages/bun-types/ffi.d.ts Outdated
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the latest review in e55322e: the runtime FFIType lookup object now has reverse entries for 18, 19, and 20, so declaring argument types with the FFIType.napi_env / FFIType.napi_value enum values works through FFIBuilder (previously "Unsupported type 18"). The cc napi passthrough test now uses the enum spellings to cover that path. Note that FFIType.buffer (numeric 20) is still rejected earlier by the native ABI parser on every entry point (pre-existing, covered by #31449); the JS-side entry is included so it starts working when that lands. Also trimmed the oversized comment in ffi.d.ts.

@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Status: all FFI tests and type checks are green on every lane, three rounds of review feedback are addressed (FFIType string map alignment, numeric napi/buffer lookup entries, size_t spelling, sliced-CString pointer offset), and every review thread is resolved.

The remaining red is unrelated to this diff and differs per run:

  • build 66503 (current head 58fd5a7): test/cli/update_interactive_install.test.ts on Windows x64 (1 retry), a bun install CLI flake.
  • earlier runs: darwin-26-aarch64 infra/timeout failures (artifact download timeout, HTTP test timeouts) and the test/bake dev-server flake on Windows.
  • bun-plugin-svelte (GitHub check) fails identically on main.

Nothing left to address on my side; ready for review.

Comment thread src/js/bun/ffi.ts
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in d14ad60: the JS FFIType lookup now accepts "size_t" (the one native-accepted spelling it was missing), mapped to uint64_t like "usize", with a cc round-trip test and the FFITypeStringToType entry so it is representable in types. With that, every spelling the native ABI parser accepts resolves through FFIBuilder.

robobun added 3 commits June 28, 2026 19:57
Type changes (packages/bun-types/ffi.d.ts):
- dlopen, cc, linkSymbols, CFunction, and JSCallback now use const type
  parameters, so argument arity is checked from the symbol definitions
  without requiring "as const". Previously add(1, 2, 3, 4) compiled fine
  for args: ["i32", "i32"].
- CFunction returns a typed call signature instead of CallableFunction.
- JSCallback infers the callback's parameter and return types from the
  definition. cstring arguments are typed as raw Pointer | null because
  the native trampoline does not wrap them in CString.
- ptr/cstring arguments accept ArrayBuffer and DataView, which the
  runtime converts.

Runtime fixes (src/js/bun/ffi.ts):
- cc() read symbol definitions from options[key] instead of
  options.symbols[key], so FFIBuilder was never applied: cstring returns
  came back as raw pointer numbers, large uint32 arguments were
  corrupted (take_u32(0xFFFFFFFF) returned 4292870144), and passing a
  JSCallback object as a function argument segfaulted. This was the
  cause of the "FIXME: bus error" skips in cc.test.ts.
- Pointer arguments now accept CString (unwrapped to its ptr), matching
  what the published types have always claimed; previously this threw
  "Unable to convert hello to a pointer".
- The ArrayBufferView check now includes DataView, which the compiled
  stubs already handle (JSType range includes DataView).
- napi_env/napi_value arguments pass through unmodified instead of
  going through the default val|0 coercion, which corrupts JSValues now
  that cc applies wrappers.

Tests:
- test/js/bun/ffi/ffi.test.js: dlopen pointer-argument conversion tests
  (CString, ArrayBuffer, DataView, rejection), using libc strlen.
- test/js/bun/ffi/cc.test.ts: un-skipped the ping/strlen cstring tests
  (fixing makeValidCase, which returned the library variable before
  beforeAll assigned it), plus new coverage for cstring returns, u32,
  ArrayBuffer, JSCallback function arguments, and napi_value passthrough.
- test/integration/bun-types/fixture/ffi.ts: type-level assertions for
  inferred arity, CFunction, and JSCallback signatures.
robobun added 3 commits June 28, 2026 19:57
The runtime and the native parser both map "function", "callback", and
"fn" to FFIType.function, so string-spelled function arguments accept a
JSCallback like the enum spelling does. The string map also gains the
other spellings the runtime accepts (c_int, c_uint, isize, char*, void*,
i64_fast, u64_fast).

Also trims a comment in cc.test.ts to the repo's 3 line limit and drops
a non-null assertion in the docs example that had no effect.
…ppers

The FFIType lookup object only had reverse entries for 0 through 17, so
declaring an argument with FFIType.napi_env, FFIType.napi_value, or
FFIType.buffer (the numbers, not the string spellings) made FFIBuilder
throw "Unsupported type". For dlopen and linkSymbols this was a
pre-existing bug; for cc it would have become reachable once symbol
definitions are read from options.symbols, so the napi passthrough test
now declares its argument types with the enum values.
The native ABI parser maps "size_t" to uint64_t but the JS lookup object
did not, so FFIBuilder threw "Unsupported type size_t" for dlopen and,
once cc applies wrappers, for cc as well. Also exposes the spelling in
FFITypeStringToType.
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main again. One conflict: main independently landed the same tsgo entrypoint fix in test/integration/bun-types/bun-types.test.ts (e2a69b7), so this branch's version of that commit was dropped in favor of main's and the file no longer differs from main. No other FFI changes landed upstream; both FFI test files pass locally after the rebase.

@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: 4

🤖 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 `@docs/runtime/ffi.mdx`:
- Around line 293-299: Document the nullable pointer contract in the JSCallback
examples by updating the callback parameter types and surrounding explanation so
they reflect that "ptr" may be Pointer | null. In the JSCallback sample(s) shown
in the ffi.mdx docs, adjust the signature and usage guidance to avoid
dereferencing the pointer without a null check, and make sure the text clearly
states when a callback may receive null so readers don’t copy an unsafe pattern.

In `@packages/bun-types/ffi.d.ts`:
- Around line 430-433: The callback return docs for the FFI pointer-typed return
behavior are missing DataView even though the type accepts it, so update the
documentation near the return type description in ffi.d.ts to explicitly mention
DataView alongside TypedArray; use the surrounding callback/return docs and
Pointer, CString, and JSCallback references to keep the wording aligned with the
public contract.

In `@src/js/bun/ffi.ts`:
- Around line 313-315: Replace the `instanceof __GlobalBunCString` check in the
`CString` handling path with a private brand check or static helper on
`__GlobalBunCString`, so detection cannot be affected by `Symbol.hasInstance` or
prototype tampering. Update the logic in the branch that returns `val.ptr` to
use the class’s internal brand/slot-based check, and keep the same behavior for
valid `CString` instances.

In `@test/js/bun/ffi/cc.test.ts`:
- Around line 155-163: The current CString test only covers the zero-offset case
and can miss the byteOffset bug in bun/ffi argument unwrapping. Extend the
existing `it("given a valid CString, returns a CString wrapping the same
pointer", ...)` case to also create a sliced `CString` with a non-zero offset
(for example via `new CString(ptr(arr), 2, 5)`), pass it through
`holder.library.symbols.ping`, and assert on the returned bytes/string content
rather than comparing base pointers. Keep the checks focused on observable
semantics so `src/js/bun/ffi.ts` is validated against offset-aware behavior.
🪄 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: 76953651-d014-4874-b70a-e4e121e14d98

📥 Commits

Reviewing files that changed from the base of the PR and between d14ad60 and 8819d00.

📒 Files selected for processing (6)
  • docs/runtime/ffi.mdx
  • packages/bun-types/ffi.d.ts
  • src/js/bun/ffi.ts
  • test/integration/bun-types/fixture/ffi.ts
  • test/js/bun/ffi/cc.test.ts
  • test/js/bun/ffi/ffi.test.js

Comment thread docs/runtime/ffi.mdx
Comment thread packages/bun-types/ffi.d.ts Outdated
Comment thread src/js/bun/ffi.ts Outdated
Comment thread test/js/bun/ffi/cc.test.ts
A CString constructed with a byteOffset represents the string at
ptr + byteOffset, so passing one as a pointer argument now forwards that
address instead of the base pointer. The check also no longer relies on
instanceof, matching how the function wrapper detects pointer carriers.

Also mentions DataView in the callback return type docs and makes the
JSCallback docs examples handle a null pointer argument.
@robobun

robobun commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed all four review comments in 58fd5a7:

  • Real bug: passing a CString constructed with a byteOffset forwarded the base pointer instead of the address of its data. The wrapper now forwards ptr + byteOffset (consistent with CString's arrayBuffer getter), covered by a sliced-CString cc test and a sliced-CString dlopen strlen test that fail against the base-pointer behavior.
  • The CString detection no longer uses instanceof; it duck-types a numeric .ptr own property, the same approach the function-argument wrapper in this file already uses, so Symbol.hasInstance and prototype tampering are irrelevant.
  • Mentioned DataView in the callback return JSDoc.
  • The JSCallback docs examples now take Pointer | null and guard before constructing a CString.

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: #35246 replaced the implementation this PR patches. dlopen, linkSymbols, CFunction and JSCallback now go through the engine-native FFI, the JS wrapper layer in src/js/bun/ffi.ts that this PR fixed no longer exists, and the branch conflicts with main. Both linked issues are closed: #235 (dlopen and friends are generic over their symbol table, so parameter and return types are inferred per symbol) and #24518 (cstring arguments accept a string directly, so wrapping in CString is no longer needed).

Two of the cc() problems described here still reproduce on current canary, because cc() still uses the TinyCC trampoline: u32 arguments and returns at or above 2^31 come back wrong (identity_u32(0xFFFFFFFF) returns 4292870144, a function returning 0x80000000 yields -2147483648), and passing a JSCallback object as a function argument segfaults, while the same declarations through dlopen behave correctly. Those need a fix in the trampoline conversions (src/runtime/ffi/FFI.h, src/runtime/ffi/abi_type.rs) rather than in this branch, and are being handled separately.

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.

Cannot pass CString to FFIType.cstring FFI Type Inference

2 participants