Changelog#
History#
Koffi 2.1.1#
Main fixes:
Fix potential memory allocation bugs
Koffi 2.1.0#
Main changes:
Add koffi.as() to support polymorphic APIs based on
void *
parametersAdd endian-sensitive integer types:
intX_le_t
,intX_be_t
,uintX_le_t
,uintX_be_t
Accept typed arrays for
void *
parametersIntroduce
koffi.opaque()
to replacekoffi.handle()
(which remains supported until Koffi 3.0)Support JS Array and TypedArray to fill struct and array pointer members
Other changes:
Improve global performance with inlining and unity builds
Add
size_t
primitive typeSupport member-specific alignement values in structs
Detect impossible parameter and return types (such as non-pointer opaque types)
Various documentation fixes and improvements
Koffi 2.0.1#
Main changes:
Return
undefined
(instead of null) forvoid
functions
Koffi 2.0.0#
Major new features:
Add disposable types for automatic disposal of C values (such as heap-allocated strings)
Add support for registered callbacks, that can be called after the initial FFI call
Support named pointer types
Support complex type specifications outside of prototype parser
Minor new features:
Support type aliases with
koffi.alias()
Add
koffi.resolve()
to resolve type stringsExpose all primitive type aliases in
koffi.types
Correctly pass exceptions thrown in JS callbacks
Breaking API changes:
Change handling of callback types, which must be used through pointers
Change handling of opaque handles, which must be used through pointers
Support all types in
koffi.introspect(type)
Consult the migration guide for more information.
Koffi 1.3.12#
Main fixes:
Fix support for Yarn package manager
Koffi 1.3.11#
Main fixes:
Fix broken parsing of
void *
when used for first parameter
Koffi 1.3.10#
Main fixes:
Fix support for callbacks with more than 4 parameters on Windows x64
Fix support for callbacks with multiple floating-point arguments on ARM32 platforms
Fix possibly incorrect conversion for uint32_t callback parameters
Other changes:
Various documentation fixes and improvements
Koffi 1.3.9#
Main fixes:
Fix prebuild compatibility with Electron on Windows x64
Koffi 1.3.8#
Main changes:
Prevent callback reuse beyond FFI call
Add BTI support for AAarch64 platforms (except Windows)
Other changes:
Fix and harmonize a few error messages
Koffi 1.3.7#
Main fixes:
Fix crash when using callbacks inside structs
Support for null strings in record members
Other changes:
Add intptr_t and uintptr_t primitive types
Add str/str16 type aliases for string/string16
Various documentation fixes and improvements
Koffi 1.3.6#
Main fixes:
Fix install error with Node < 15 on Windows (build system bug)
Other changes:
Detect incompatible Node.js versions when installing Koffi
Prebuild with Clang for Windows x64 and Linux x64 binaries
Various documentation improvements
Koffi 1.3.5#
Main changes:
Fix memory leak when many async calls are running
Add configurable limit for maximum number of async calls (max_async_calls)
Other changes:
Reduce default async memory stack and heap size
Various documentation improvements
Koffi 1.3.4#
Main fixes:
Fix possible OpenBSD i386 crash with
(void)
functions
Koffi 1.3.3#
Main fixes:
Fix misconversion of signed integer return value as unsigned
Other changes:
Support
(void)
(empty) function signaturesDisable unsafe compiler optimizations
Various documentation improvements
Koffi 1.3.2#
Main fixes:
Support compilation in C++14 mode (graceful degradation)
Support older toolchains on Linux (tested on Debian 9)
Koffi 1.3.1#
Main fixes:
The prebuilt binary is tested when Koffi is installed, and a rebuild happens if it fails to load
Koffi 1.3.0#
Major changes:
Expand and move documentation to https://koffi.dev/
Support JS arrays and TypedArrays for pointer arguments (input, output and mixed)
Other changes:
Convert NULL string pointers to null instead of crashing (return values, struct and array members, callbacks)
Default to ‘string’ array hint for char, char16 and char16_t arrays
Fix definition of long types on Windows x64 (LLP64 model)
Restrict automatic string conversion to signed char types
Detect floating-point ABI before using prebuilt binaries (ARM32, RISC-V)
Forbid duplicate member names in struct types
Koffi 1.2.4#
New features:
Windows ARM64 is now supported
Koffi 1.2.3#
New features:
A prebuilt binary for macOS ARM64 (M1) is now included
Koffi 1.2.1#
This entry documents changes since version 1.1.0.
New features:
JS functions can be used as C callbacks (cdecl, stdcall) on all platforms
RISC-V 64 LP64D ABI is supported (LP64 is untested)
Expose settings for memory usage of synchronous and asynchronous calls
Transparent conversion between C buffers and strings
Tentative support for Windows ARM64 (untested)
Main fixes:
Fix excessive stack alignment of structs on x86 platforms
Fix potential problems with big int64_t/uint64_t values
Fix possible struct layout errors in push/pop code
Fix alignment issues in ARM32 push code
Fix incomplete/buggy support for HFA structs on ARM32 and ARM64
Fix crashes on OpenBSD caused by missing MAP_STACK flag
Fix non-sense “duplicate array type” errors
Fix value
koffi.internal
to false in module (normal) buildsMake sure we have a redzone below the stack for all architectures
Use slower allocation for big objects instead of failing
Migration guide#
Koffi 1.x to 2.x#
The API was changed in 2.x in a few ways, in order to reduce some excessively “magic” behavior and reduce the syntax differences between C and the C-like prototypes.
You may need to change your code if you use:
Callback functions
Opaque types
koffi.introspect()
Callback types#
In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer. Now, you must use them through a pointer: void CallIt(CallbackType func)
in Koffi 1.x becomes void CallIt(CallbackType *func)
in version 2.0 and newer.
Given the following C code:
1#include <string.h>
2
3int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
4{
5 char buf[64];
6 snprintf(buf, sizeof(buf), "Hello %s!", str);
7 return cb(buf, age);
8}
The two versions below illustrate the API difference between Koffi 1.x and Koffi 2.x:
1// Koffi 1.x
2
3const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
4
5const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', TransferCallback]);
6// Equivalent to: const TransferToJS = lib.func('int TransferToJS(str s, int x, TransferCallback cb)');
7
8let ret = TransferToJS('Niels', 27, (str, age) => {
9 console.log(str);
10 console.log('Your age is:', age);
11 return 42;
12});
13console.log(ret);
1// Koffi 2.x
2
3const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
4
5const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
6// Equivalent to: const TransferToJS = lib.func('int TransferToJS(str s, int x, TransferCallback *cb)');
7
8let ret = TransferToJS('Niels', 27, (str, age) => {
9 console.log(str);
10 console.log('Your age is:', age);
11 return 42;
12});
13console.log(ret);
Koffi 1.x only supported transient callbacks, you must use Koffi 2.x for registered callbacks.
Opaque types#
In Koffi 1.x, opaque handles were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer. Now, in Koffi 2.0, you must use them through a pointer, and use an array for output parameters.
In addition to that, koffi.handle()
has been deprecated in Koffi 2.1 and replaced with koffi.opaque()
. They work the same but new code should use koffi.opaque()
, the former one will eventually be removed in Koffi 3.0.
For functions that return opaque pointers or pass them by parameter:
1// Koffi 1.x
2
3const FILE = koffi.handle('FILE');
4const fopen = lib.func('fopen', 'FILE', ['str', 'str']);
5const fopen = lib.func('fclose', 'int', ['FILE']);
6
7let fp = fopen('EMPTY', 'wb');
8if (!fp)
9 throw new Error('Failed to open file');
10fclose(fp);
1// Koffi 2.1
2
3// If you use Koffi 2.0: const FILE = koffi.handle('FILE');
4const FILE = koffi.opaque('FILE');
5const fopen = lib.func('fopen', 'FILE *', ['str', 'str']);
6const fopen = lib.func('fclose', 'int', ['FILE *']);
7
8let fp = fopen('EMPTY', 'wb');
9if (!fp)
10 throw new Error('Failed to open file');
11fclose(fp);
For functions that set opaque handles through output parameters (such as sqlite3_open_v2
), you must now use a single element array as shown below:
1// Koffi 1.x
2
3const sqlite3 = koffi.handle('sqlite3');
4
5const sqlite3_open_v2 = lib.func('int sqlite3_open_v2(const char *, _Out_ sqlite3 *db, int, const char *)');
6const sqlite3_close_v2 = lib.func('int sqlite3_close_v2(sqlite3 db)');
7
8const SQLITE_OPEN_READWRITE = 0x2;
9const SQLITE_OPEN_CREATE = 0x4;
10
11let db = {};
12
13if (sqlite3_open_v2(':memory:', db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
14 throw new Error('Failed to open database');
15
16sqlite3_close_v2(db);
1// Koffi 2.1
2
3// If you use Koffi 2.0: const sqlite3 = koffi.handle('sqlite3');
4const sqlite3 = koffi.opaque('sqlite3');
5
6const sqlite3_open_v2 = lib.func('int sqlite3_open_v2(const char *, _Out_ sqlite3 **db, int, const char *)');
7const sqlite3_close_v2 = lib.func('int sqlite3_close_v2(sqlite3 *db)');
8
9const SQLITE_OPEN_READWRITE = 0x2;
10const SQLITE_OPEN_CREATE = 0x4;
11
12let db = null;
13
14let ptr = [null];
15if (sqlite3_open_v2(':memory:', ptr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
16 throw new Error('Failed to open database');
17db = ptr[0];
18
19sqlite3_close_v2(db);
koffi.introspect()#
In Koffi 1.x, koffi.introspect()
would only work with struct types, and return the object passed to koffi.struct()
to initialize the type. Now this function works with all types.
You can still get the list of struct members:
1const StructType = koffi.struct('StructType', { dummy: 'int' });
2
3// Koffi 1.x
4let members = koffi.introspect(StructType);
5
6// Koffi 2.x
7let members = koffi.introspect(StructType).members;