Typed calls
CFunction prepares the libffi call interface once and returns a plain JS function. Types follow bun:ffi: i32, u64 (exact bigint), f64, pointer, cstring.
Load a shared library, describe a prototype, call it. qjs-ffi is a small loadable QuickJS module built on libffi, with a bun:ffi-style API, native-to-JS callbacks, and a generator that turns a C header into a ready-made binding.
import { dlopen } from 'ffi';
const { symbols } = dlopen(null, {
strlen: { args: ['cstring'], returns: 'u64_fast' },
getenv: { args: ['cstring'], returns: 'cstring' },
});
console.log(symbols.strlen('hello, world')); // 12
console.log(symbols.getenv('HOME'));Writing a QuickJS binding in C for every library is a lot of code for a few calls. This is the shortcut.
CFunction prepares the libffi call interface once and returns a plain JS function. Types follow bun:ffi: i32, u64 (exact bigint), f64, pointer, cstring.
JSCallback turns a JS function into a real native function pointer, so C code can call back into your script with properly marshalled arguments.
lib/ ships generated bindings for cairo, SDL2, freetype, libcurl, libusb, libarchive and zlib. tools/gen-bindings.js makes one for any other header, using clang's AST.
Needs libffi and libdl, and QuickJS to load the module into.
git clone https://github.com/rsenn/qjs-ffi.git
cd qjs-ffi
cmake -B build && cmake --build build
qjsm examples/cairo.js