Skip to content

fix(core): pass transient FFI buffers directly - #1394

Merged
simonklee merged 2 commits into
mainfrom
ffi-buffer-args
Aug 20, 2026
Merged

simonklee merged 2 commits into
mainfrom
ffi-buffer-args

Conversation

@Hona

@Hona Hona commented Aug 20, 2026 •

Copy link
Copy Markdown
Member

Summary

  • use buffer ABI parameters and pass typed-array owners directly for transient synchronous Bun FFI calls
  • lower portable buffer parameters to Node's stable pointer path while still passing owner objects directly
  • keep ptr only for nullable, mixed native-pointer, raw ArrayBuffer, callback, and retained-memory cases
  • stabilize retained text-memory views through .buffer before resolving their native address
  • preserve raw-pointer compatibility while allowing direct buffers in supersample, packed-buffer, matrix, and grayscale APIs
  • document the Bun 1.3.14 and Bun 1.4+ ownership rules in AGENTS.md

Why

A Bun FastTypedArray may store its data inline. Calling ptr(view) captures that address, but a later first access to view.buffer can move the data into separate ArrayBuffer storage and leave the native address stale. Passing the owner directly lets the FFI backend borrow the current storage for synchronous calls and allows Bun's buffer fast path to keep the owner visible to the JIT.

This keeps nullable and true native-pointer parameters on ptr, where buffer cannot represent the ABI, while avoiding pre-resolved addresses for transient memory. Retained pointers explicitly materialize stable backing storage before ptr(view) and keep the view alive for the native lifetime.

Node 26.4's Linux optimized buffer trampoline delivered a null pointer to a multi-argument audio call in CI. OpenTUI therefore maps its portable buffer descriptor to Node pointer, whose documented owner-borrowing path passes the same views correctly. Bun continues to receive the buffer descriptor.

Related Bun investigation: oven-sh/bun#32054 and oven-sh/bun#32055.

Testing

  • bunx bun@1.3.14 test src/tests/ffi-borrowed-pointer-callsites.test.ts (23 passed)
  • bun test src/tests/ffi-borrowed-pointer-callsites.test.ts on Bun 1.4 canary (23 passed)
  • bun run test:js (5,397 passed, 23 skipped)
  • bun run test:js:node with Node 26.4.0 (4,665 passed, 6 skipped)
  • bun run test:dist
  • bun run build:lib
  • bun run fmt:check
  • bun run lint

Copilot AI lite review requested due to automatic review settings August 20, 2026 10:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@kommander kommander left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

@simonklee
simonklee merged commit 2e7f96f into main Aug 20, 2026
32 of 34 checks passed
@simonklee
simonklee deleted the ffi-buffer-args branch August 20, 2026 13:42
carsteneu added a commit to carsteneu/opentui that referenced this pull request Aug 20, 2026
…deps, embedded terminal, FFI buffer types

- zig.ts: kept wave5 staged binding (opentuiSymbolDefs 411 = 395 ours + 16 embeddedTerminal),
  adopted upstream descriptor changes (ptr->buffer anomalyco#1394, destroyRenderer +flushInput)
- console.ts: refcount activate() combined with upstream OTUI_USE_CONSOLE env gate
- package.json/.gitignore/.oxfmtrc: upstream native-package layout + our wave3 gates
- renderer.console-startup.test: both our refcount tests and upstream env-gate test
carsteneu pushed a commit to carsteneu/opentui that referenced this pull request Aug 20, 2026
## Summary

- use `buffer` ABI parameters and pass typed-array owners directly for
transient synchronous Bun FFI calls
- lower portable `buffer` parameters to Node's stable `pointer` path
while still passing owner objects directly
- keep `ptr` only for nullable, mixed native-pointer, raw `ArrayBuffer`,
callback, and retained-memory cases
- stabilize retained text-memory views through `.buffer` before
resolving their native address
- preserve raw-pointer compatibility while allowing direct buffers in
supersample, packed-buffer, matrix, and grayscale APIs
- document the Bun 1.3.14 and Bun 1.4+ ownership rules in `AGENTS.md`

## Why

A Bun `FastTypedArray` may store its data inline. Calling `ptr(view)`
captures that address, but a later first access to `view.buffer` can
move the data into separate `ArrayBuffer` storage and leave the native
address stale. Passing the owner directly lets the FFI backend borrow
the current storage for synchronous calls and allows Bun's `buffer` fast
path to keep the owner visible to the JIT.

This keeps nullable and true native-pointer parameters on `ptr`, where
`buffer` cannot represent the ABI, while avoiding pre-resolved addresses
for transient memory. Retained pointers explicitly materialize stable
backing storage before `ptr(view)` and keep the view alive for the
native lifetime.

Node 26.4's Linux optimized `buffer` trampoline delivered a null pointer
to a multi-argument audio call in CI. OpenTUI therefore maps its
portable `buffer` descriptor to Node `pointer`, whose documented
owner-borrowing path passes the same views correctly. Bun continues to
receive the `buffer` descriptor.

Related Bun investigation: oven-sh/bun#32054 and oven-sh/bun#32055.

## Testing

- `bunx bun@1.3.14 test
src/tests/ffi-borrowed-pointer-callsites.test.ts` (23 passed)
- `bun test src/tests/ffi-borrowed-pointer-callsites.test.ts` on Bun 1.4
canary (23 passed)
- `bun run test:js` (5,397 passed, 23 skipped)
- `bun run test:js:node` with Node 26.4.0 (4,665 passed, 6 skipped)
- `bun run test:dist`
- `bun run build:lib`
- `bun run fmt:check`
- `bun run lint`
carsteneu added a commit to carsteneu/opentui that referenced this pull request Aug 20, 2026
…deps, embedded terminal, FFI buffer types

- zig.ts: kept wave5 staged binding (opentuiSymbolDefs 411 = 395 ours + 16 embeddedTerminal),
  adopted upstream descriptor changes (ptr->buffer anomalyco#1394, destroyRenderer +flushInput)
- console.ts: refcount activate() combined with upstream OTUI_USE_CONSOLE env gate
- package.json/.gitignore/.oxfmtrc: upstream native-package layout + our wave3 gates
- renderer.console-startup.test: both our refcount tests and upstream env-gate test
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.

4 participants