Skip to content

ffi: Refactor to simpler API.#4

Merged
cjihrig merged 2 commits into
cjihrig:ffifrom
ShogunPanda:ffi
Mar 17, 2026
Merged

ffi: Refactor to simpler API.#4
cjihrig merged 2 commits into
cjihrig:ffifrom
ShogunPanda:ffi

Conversation

@ShogunPanda

Copy link
Copy Markdown

Hi Colin!
After chatting with Matteo, we would like to propose a simplified API.

This is also inspired by Bun existing API.

I started this PR on top of yours.

This explicitly manipulates pointers and raw memory and thus avoid need for many wrapper objects that do not provide additional safety.

API examples

Before

const { dlopen, UnsafeFnPointer, UnsafeCallback, UnsafePointerView } = require('node:ffi');

const lib = dlopen('./mylib.so', {
  add_i32: { parameters: ['i32', 'i32'], result: 'i32' },
});

console.log(lib.symbols.add_i32(20, 22));

After

const { dlopen, DynamicLibrary, getInt32, setInt32, toString } = require('node:ffi');

const { lib, functions } = dlopen('./mylib.so', {
  add_i32: { parameters: ['i32', 'i32'], result: 'i32' },
});

console.log(functions.add_i32(20, 22));

const ptr = lib.registerCallback({ parameters: ['i32'], result: 'i32' }, (n) => n * 2);
setInt32(address, 0, 42);
console.log(getInt32(address, 0));
console.log(toString(cStringPtr));
lib.unregisterCallback(ptr);

Main changes

  • The API now centers on DynamicLibrary and raw bigint pointers.
  • The old UnsafePointer, UnsafePointerView, UnsafeFnPointer, UnsafeCallback, and struct-descriptor APIs are removed.
  • dlopen() now returns { lib, functions }: callable wrappers live in functions, while resolved symbol addresses are exposed as bigints.
  • Added top-level helpers like dlclose(), dlsym(), get*() / set*(), toString(), toBuffer(), toArrayBuffer(), exportString(), and exportBuffer().
  • Callbacks are now owned by the library handle via registerCallback() / unregisterCallback() / refCallback() / unrefCallback().
  • Native code was consolidated into a simpler implementation around src/node_ffi.* plus shared type/memory helpers.
  • Build/configuration was updated: FFI is now opt-out with --without-ffi, with added support for shared libffi via --shared-ffi* options.
  • Permission checks now cover both library loading and raw memory helpers.
  • Tests and docs were rewritten to match the new API shape.
  • All Tier 1 architectures are now supported.
  • Support for dynamically linked libffi.

Signed-off-by: Paolo Insogna <paolo@cowtech.it>
Signed-off-by: Paolo Insogna <paolo@cowtech.it>
@cjihrig cjihrig merged commit fe8170e into cjihrig:ffi Mar 17, 2026
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.

2 participants