ffi qjs-ffi
libffi + dlopen for QuickJS

qjs-ffi: call C from JavaScript, no glue code

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'));

Why qjs-ffi

Writing a QuickJS binding in C for every library is a lot of code for a few calls. This is the shortcut.

01

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.

02

Callbacks both ways

JSCallback turns a JS function into a real native function pointer, so C code can call back into your script with properly marshalled arguments.

03

Bindings included

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.

Install

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