You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The single biggest platform feature for compiling Bun-specific apps (driver: opencode, but this is what the Bun-native ecosystem standardizes on): a C-ABI FFI layer with the bun:ffi API shape (and optionally Node's experimental node:ffi shape — bun-ffi-structs already bridges both, so one implementation can serve either import).
Evidence from opencode
Everything native in the TUI product funnels through plain C-ABI dylibs — no N-API anywhere (verified: libopentui.dylib from @opentui/core-darwin-arm64@0.4.5 exports 1,775 C symbols, zero napi_* entries; the "node backend" is the same dylib driven via experimental node:ffi):
toArrayBuffer(ptr, byteOffset, byteLength) — 28 uses in opentui — zero-copy JS views over native-owned memory; needs external backing-store support in perry's ArrayBuffer (no GC ownership, explicit lifetime).
CString (read NUL-terminated, encode).
JSCallback / FFIType.function — native→JS callbacks (opentui registers them). Hardest piece: trampoline into perry's JS execution, closure rooting, and a decision on threading (reject cross-thread invocation initially unless opentui proves to need it — verify which callbacks it actually registers before building).
bun-ffi-structs is pure JS over these primitives — free once they exist.
For a native-code runtime, this is natural territory: direct dlopen + call-stub generation (libffi or generated thunks), no JIT trampolines needed.
Suggested bring-up
dlopen + typed calls + ptr on pinned buffers + CString, proven against bun-pty's 17-symbol dylib (spawn a shell, echo round-trip) — small, observable, exercises strings/ints/pointers/read-loop.
toArrayBuffer external backing stores.
JSCallback trampolines.
opentui's 348-symbol table + renderer bring-up (the opencode target tracker owns the app-level milestone).
Acceptance per stage; stage 1 acceptance: a perry-compiled script drives bun-pty end-to-end on macOS-arm64 + linux-x64.
The new full-source OpenCode target is tracked by #8506. This remains its P0 native boundary: complete callbacks/closure rooting, external memory views, and node:ffi compatibility so the unmodified OpenTUI/Yoga and FFF wrappers can compile and use their upstream dylibs.
Closed by #8704, landed on main via #8721 (squash 3f655301e).
That PR was landed through a batch wrapper PR rather than merged directly, so GitHub's close keyword never fired at merge time — its mergedAt is null even though the work is on main. Verified before closing: crates/perry-runtime/src/bun_ffi/ (call, callback, dlopen, memory, read) is present on main, changelog.d/8704-complete-c-abi-ffi.md landed, and its 29 bun_ffi tests pass.
The single biggest platform feature for compiling Bun-specific apps (driver: opencode, but this is what the Bun-native ecosystem standardizes on): a C-ABI FFI layer with the
bun:ffiAPI shape (and optionally Node's experimentalnode:ffishape —bun-ffi-structsalready bridges both, so one implementation can serve either import).Evidence from opencode
Everything native in the TUI product funnels through plain C-ABI dylibs — no N-API anywhere (verified:
libopentui.dylibfrom@opentui/core-darwin-arm64@0.4.5exports 1,775 C symbols, zeronapi_*entries; the "node backend" is the same dylib driven via experimentalnode:ffi):@opentui/core(the TUI renderer)libopentui.dylibper platformYG*) + embedded tree-sitter with bundled grammars (js/ts/markdown/zig)bun-ptylibrust_pty.{dylib,so,dll}(bundled in tarball)@ff-labs/fff-bun@ff-labs/fff-bin-*platform pkgssrc/ffi.tsPrimitives required (from actual usage, not the full spec)
dlopen(path, symbolTable)with typed args/returns —FFIType.{void, ptr, pointer, cstring, bool, i8..i64, u8..u64, usize, f32, f64, function, callback}(observed set in@opentui/coredist).ptr(arrayBufferOrView)— hand JS buffer memory to native. GC design constraint: perry's GC relocates objects (cf. the forwarding-stub/array-growth work, Default GC: array from a direct function call, stored in a variable, is lost when its backing store grows past 32MiB; object arrays get corrupted length #6228/fix(gc): minor sweep reclaimed array-growth forwarding stubs still referenced from old-gen parents #6471) — FFI-exposed buffers must be pinned or allocated in non-moving/off-heap space for their native lifetime.toArrayBuffer(ptr, byteOffset, byteLength)— 28 uses in opentui — zero-copy JS views over native-owned memory; needs external backing-store support in perry's ArrayBuffer (no GC ownership, explicit lifetime).CString(read NUL-terminated, encode).JSCallback/FFIType.function— native→JS callbacks (opentui registers them). Hardest piece: trampoline into perry's JS execution, closure rooting, and a decision on threading (reject cross-thread invocation initially unless opentui proves to need it — verify which callbacks it actually registers before building).bun-ffi-structsis pure JS over these primitives — free once they exist.For a native-code runtime, this is natural territory: direct dlopen + call-stub generation (libffi or generated thunks), no JIT trampolines needed.
Suggested bring-up
ptron pinned buffers +CString, proven against bun-pty's 17-symbol dylib (spawn a shell, echo round-trip) — small, observable, exercises strings/ints/pointers/read-loop.toArrayBufferexternal backing stores.JSCallbacktrampolines.Acceptance per stage; stage 1 acceptance: a perry-compiled script drives bun-pty end-to-end on macOS-arm64 + linux-x64.