Skip to main content
Requires the ffi permission in your manifest:
The FFI module lets you load native libraries (.dll, .so) and call their functions directly from JavaScript - no bindings, no wrappers.
Pointers are represented as BigInt values. A 0n BigInt is a null pointer. The engine never uses raw number for addresses.

Overview

Libraries & functions

Load a DLL/SO, resolve symbols, call native functions.

Structs & unions

Define C-compatible data layouts and access fields directly.

Memory

Allocate, read, write, and convert pointers.

Callbacks

Pass JavaScript functions where native code expects a function pointer.

Types

Supported type names and how to define aliases.

Resource management

Auto-cleanup scopes and disposable handles.

Libraries & functions

Load a native library, then pull functions out of it. Two styles: quick proc calls for simple signatures, or C-style func declarations when you want readable code.

open


Loads a dynamic library and returns a library handle. On Windows this calls LoadLibraryA, on Linux/macOS dlopen.
The library stays loaded until you call close() or the script ends. Forgetting to close can leak handles across reloads.

close


Unloads the library. Any functions resolved from it become invalid - calling them after close() will crash.

proc


Resolves a symbol by name and wraps it as a callable function. The quick-and-dirty way - pass types as separate arguments.

func


Resolves a function using a C-style signature string. More readable than proc for complex declarations. The signature format is returnType [abi] functionName(paramTypes...):
Supported calling conventions: __cdecl (default), __stdcall, __fastcall, __thiscall. Prefix the function name with the keyword.

funcs


Batch-declare multiple functions at once. Returns an object with a callable for each key.
If the signature already contains a function name, the symbol is resolved by that name. The object key just becomes the JS alias. So sleep in the example above resolves Sleep from the DLL.

funcAsync / funcsAsync


Async variants of func and funcs. The native call runs on a worker thread and returns a Promise that resolves with the result.
Variadic functions are not supported in async mode. Only use funcAsync for non-variadic signatures.

Structs & unions

Define C-compatible memory layouts. Struct instances are backed by an ArrayBuffer - fields are live accessors into the buffer, not copies. Write a field and the native memory updates immediately.

struct


Defines a struct layout. Field order matches property insertion order. Returns a type object with factory methods.
Array fields use bracket notation:
Nested structs reference by name:
char[N] fields are treated as C strings - they read/write as JS strings, auto null-terminated at N-1.
The returned type object has these methods:
view() wraps raw memory with no bounds checking beyond the struct’s own size. A bad pointer will read garbage or crash. Always validate addresses before calling view.

union


Like struct, but all fields share the same memory (offset 0). The total size equals the largest field.
The returned type has alloc(), from(), and view() - same as struct.

Memory

Raw memory operations. Use these when you need to go below struct-level - reading strings from pointers, allocating scratch buffers, or converting between pointer representations.

alloc


Allocates a zeroed ArrayBuffer of size bytes. Maximum 256 MB.

free


Frees memory previously allocated by native code (via the C runtime’s free). Silent no-op on null / undefined.
Only use FFI.free on pointers returned by native allocators (e.g. malloc). Never free an ArrayBuffer pointer - the JS garbage collector owns those.

toPointer


Converts any pointer-like value to a BigInt address.

readString


Reads a null-terminated C string from a pointer. Returns null if the pointer is null. Default maxLen is 4096, capped at 64 MB.

writeString


Writes a UTF-8 string into a buffer, null-terminated. Returns the number of bytes written (excluding the null terminator).

sizeOf


Returns the byte size of any type name, including registered structs and unions.

errno


Returns the current C errno value. Check it right after a native call.

nullptr


A null pointer constant. Pass it wherever a native function expects a NULL.

Callbacks

Pass a JavaScript function where native code expects a function pointer. The engine creates a native trampoline that invokes your JS function when the native side calls through the pointer.

callback


Returns a BigInt pointer to a native-callable trampoline. Two forms - signature string or explicit types.
Callbacks are not garbage collected. You must call freeCallback when done, or they leak. The only exception is if the callback lives for the entire script lifetime.
Callbacks have a re-entry depth limit of 16. If native code calls your callback recursively beyond that, the engine returns zero/null instead of calling JS.

freeCallback


Frees a previously created callback. Throws if the callback is currently executing (mid-call).

Type system

Type names are strings used throughout the FFI API - in signatures, struct fields, proc calls, and sizeOf. The engine recognizes C-style names, Rust-style names, and Windows typedefs.

Primitive types

Pointer types

When a function returns string or wstring, the engine reads the C string and gives you a JS string. When you pass a JS string as a pointer argument, the engine auto-converts it to a temporary null-terminated C string for the duration of the call.

Composite types

Struct and union names become valid type names after definition:

typedef


Creates a type alias. Resolved recursively (up to 32 levels).

Resource management

Native resources (allocated memory, file handles, callbacks) don’t get garbage collected. These helpers prevent leaks.

disposable


Every function returned by func / proc has a .disposable() method. It returns a new function that wraps return values in a managed handle which auto-closes inside FFI.using() scopes.
The managed handle has:
The ! suffix on a return type (e.g. string!) signals that the return value is heap-allocated and should be freed. It’s a convention, not enforced - pair it with .disposable() for safety.

using


Runs fn in a managed scope. Any managed handles (from .disposable()) created during the scope are automatically closed when fn returns or throws.
FFI.using is synchronous. Don’t await inside it - the scope closes on function return, not on promise resolution.

Out parameters

Some native APIs write results through pointer parameters (like GetCursorPos(POINT*)). The FFI supports this with _Out_ and _Inout_ annotations in signatures.
For primitive out params, pass an array or { $: initialValue }:

Signature cheat sheet

A quick reference for the func signature format:

Complete example

Putting it all together - calling the Windows API to show a message box and read the cursor position: