/** * `bun:ffi` lets you efficiently call C functions & FFI functions from JavaScript * without writing bindings yourself. * * ```js * import {dlopen, CString, ptr} from 'bun:ffi'; * * const lib = dlopen('libsqlite3', { * }); * ``` * * This is powered by just-in-time compiling C wrappers * that convert JavaScript types to C types and back. Internally, * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks * goes to Fabrice Bellard and TinyCC maintainers for making this possible. * */ declare module "bun:ffi" { export enum FFIType { char = 0, /** * 8-bit signed integer * * Must be a value between -127 and 127 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * signed char * char // on x64 & aarch64 macOS * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ int8_t = 1, /** * 8-bit signed integer * * Must be a value between -127 and 127 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * signed char * char // on x64 & aarch64 macOS * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ i8 = 1, /** * 8-bit unsigned integer * * Must be a value between 0 and 255 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * unsigned char * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ uint8_t = 2, /** * 8-bit unsigned integer * * Must be a value between 0 and 255 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * unsigned char * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ u8 = 2, /** * 16-bit signed integer * * Must be a value between -32768 and 32767 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * in16_t * short // on arm64 & x64 * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ int16_t = 3, /** * 16-bit signed integer * * Must be a value between -32768 and 32767 * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * in16_t * short // on arm64 & x64 * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ i16 = 3, /** * 16-bit unsigned integer * * Must be a value between 0 and 65535, inclusive. * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * uint16_t * unsigned short // on arm64 & x64 * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ uint16_t = 4, /** * 16-bit unsigned integer * * Must be a value between 0 and 65535, inclusive. * * When passing to a FFI function (C ABI), type coercion is not performed. * * In C: * ```c * uint16_t * unsigned short // on arm64 & x64 * ``` * * In JavaScript: * ```js * var num = 0; * ``` */ u16 = 4, /** * 32-bit signed integer * */ int32_t = 5, /** * 32-bit signed integer * * Alias of {@link FFIType.int32_t} */ i32 = 5, /** * 32-bit signed integer * * The same as `int` in C * * ```c * int * ``` */ int = 5, /** * 32-bit unsigned integer * * The same as `unsigned int` in C (on x64 & arm64) * * C: * ```c * unsigned int * ``` * JavaScript: * ```js * ptr(new Uint32Array(1)) * ``` */ uint32_t = 6, /** * 32-bit unsigned integer * * Alias of {@link FFIType.uint32_t} */ u32 = 6, /** * int64 is a 64-bit signed integer * * This is not implemented yet! */ int64_t = 7, /** * i64 is a 64-bit signed integer * * This is not implemented yet! */ i64 = 7, /** * 64-bit unsigned integer * * This is not implemented yet! */ uint64_t = 8, /** * 64-bit unsigned integer * * This is not implemented yet! */ u64 = 8, /** * Doubles are not supported yet! */ double = 9, /** * Doubles are not supported yet! */ f64 = 9, /** * Floats are not supported yet! */ float = 10, /** * Floats are not supported yet! */ f32 = 10, /** * Booelan value * * Must be `true` or `false`. `0` and `1` type coercion is not supported. * * In C, this corresponds to: * ```c * bool * _Bool * ``` * * */ bool = 11, /** * Pointer value * * See {@link Bun.FFI.ptr} for more information * * In C: * ```c * void* * ``` * * In JavaScript: * ```js * ptr(new Uint8Array(1)) * ``` */ ptr = 12, /** * Pointer value * * alias of {@link FFIType.ptr} */ pointer = 12, /** * void value * * void arguments are not supported * * void return type is the default return type * * In C: * ```c * void * ``` * */ void = 13, /** * When used as a `returns`, this will automatically become a {@link CString}. * * When used in `args` it is equivalent to {@link FFIType.pointer} * */ cstring = 14, /** * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance * but means you might get a `BigInt` or you might get a `number`. * * In C, this always becomes `int64_t` * * In JavaScript, this could be number or it could be BigInt, depending on what * value is passed in. * */ i64_fast = 15, /** * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance * but means you might get a `BigInt` or you might get a `number`. * * In C, this always becomes `uint64_t` * * In JavaScript, this could be number or it could be BigInt, depending on what * value is passed in. * */ u64_fast = 16, } export type FFITypeOrString = | FFIType | "char" | "int8_t" | "i8" | "uint8_t" | "u8" | "int16_t" | "i16" | "uint16_t" | "u16" | "int32_t" | "i32" | "int" | "uint32_t" | "u32" | "int64_t" | "i64" | "uint64_t" | "u64" | "double" | "f64" | "float" | "f32" | "bool" | "ptr" | "pointer" | "void" | "cstring" | "function"; interface FFIFunction { /** * Arguments to a FFI function (C ABI) * * Defaults to an empty array, which means no arguments. * * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}. * * @example * From JavaScript: * ```js * const lib = dlopen('add', { * // FFIType can be used or you can pass string labels. * args: [FFIType.i32, "i32"], * returns: "i32", * }); * lib.symbols.add(1, 2) * ``` * In C: * ```c * int add(int a, int b) { * return a + b; * } * ``` */ args?: FFITypeOrString[]; /** * Return type to a FFI function (C ABI) * * Defaults to {@link FFIType.void} * * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}. * * @example * From JavaScript: * ```js * const lib = dlopen('z', { * version: { * returns: "ptr", * } * }); * console.log(new CString(lib.symbols.version())); * ``` * In C: * ```c * char* version() * { * return "1.0.0"; * } * ``` */ returns?: FFITypeOrString; /** * Function pointer to the native function * * If provided, instead of using dlsym() to lookup the function, Bun will use this instead. * This pointer should not be null (0). * * This is useful if the library has already been loaded * or if the module is also using Node-API. */ ptr?: number | bigint; /** * Can C/FFI code call this function from a separate thread? * * Only supported with {@link JSCallback}. * * This does not make the function run in a separate thread. It is still up to the application/library * to run their code in a separate thread. * * By default, {@link JSCallback} calls are not thread-safe. Turning this on * incurs a small performance penalty for every function call. That small * performance penalty needs to be less than the performance gain from * running the function in a separate thread. * * @default false */ threadsafe?: boolean; } type Symbols = Record; // /** // * Compile a callback function // * // * Returns a function pointer // * // */ // export function callback(ffi: FFIFunction, cb: Function): number; export interface Library { symbols: Record< string, CallableFunction & { /** * The function without a wrapper */ native: CallableFunction; } >; /** * `dlclose` the library, unloading the symbols and freeing allocated memory. * * Once called, the library is no longer usable. * * Calling a function from a library that has been closed is undefined behavior. */ close(): void; } /** * Open a library using `"bun:ffi"` * * @param name The name of the library or file path. This will be passed to `dlopen()` * @param symbols Map of symbols to load where the key is the symbol name and the value is the {@link FFIFunction} * * @example * * ```js * import {dlopen} from 'bun:ffi'; * * const lib = dlopen("duckdb.dylib", { * get_version: { * returns: "cstring", * args: [], * }, * }); * lib.symbols.get_version(); * // "1.0.0" * ``` * * This is powered by just-in-time compiling C wrappers * that convert JavaScript types to C types and back. Internally, * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks * goes to Fabrice Bellard and TinyCC maintainers for making this possible. * */ export function dlopen(name: string, symbols: Symbols): Library; /** * Turn a native library's function pointer into a JavaScript function * * Libraries using Node-API & bun:ffi in the same module could use this to skip an extra dlopen() step. * * @param fn {@link FFIFunction} declaration. `ptr` is required * * @example * * ```js * import {CFunction} from 'bun:ffi'; * * const getVersion = new CFunction({ * returns: "cstring", * args: [], * ptr: myNativeLibraryGetVersion, * }); * getVersion(); * getVersion.close(); * ``` * * This is powered by just-in-time compiling C wrappers * that convert JavaScript types to C types and back. Internally, * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks * goes to Fabrice Bellard and TinyCC maintainers for making this possible. * */ export function CFunction( fn: FFIFunction & { ptr: number | bigint }, ): CallableFunction & { /** * Free the memory allocated by the wrapping function */ close(): void; }; /** * Link a map of symbols to JavaScript functions * * This lets you use native libraries that were already loaded somehow. You usually will want {@link dlopen} instead. * * You could use this with Node-API to skip loading a second time. * * @param symbols Map of symbols to load where the key is the symbol name and the value is the {@link FFIFunction} * * @example * * ```js * import { linkSymbols } from "bun:ffi"; * * const [majorPtr, minorPtr, patchPtr] = getVersionPtrs(); * * const lib = linkSymbols({ * // Unlike with dlopen(), the names here can be whatever you want * getMajor: { * returns: "cstring", * args: [], * * // Since this doesn't use dlsym(), you have to provide a valid ptr * // That ptr could be a number or a bigint * // An invalid pointer will crash your program. * ptr: majorPtr, * }, * getMinor: { * returns: "cstring", * args: [], * ptr: minorPtr, * }, * getPatch: { * returns: "cstring", * args: [], * ptr: patchPtr, * }, * }); * * const [major, minor, patch] = [ * lib.symbols.getMajor(), * lib.symbols.getMinor(), * lib.symbols.getPatch(), * ]; * ``` * * This is powered by just-in-time compiling C wrappers * that convert JavaScript types to C types and back. Internally, * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks * goes to Fabrice Bellard and TinyCC maintainers for making this possible. * */ export function linkSymbols(symbols: Symbols): Library; /** * Read a pointer as a {@link Buffer} * * If `byteLength` is not provided, the pointer is assumed to be 0-terminated. * * @param ptr The memory address to read * @param byteOffset bytes to skip before reading * @param byteLength bytes to read * * While there are some checks to catch invalid pointers, this is a difficult * thing to do safely. Passing an invalid pointer can crash the program and * reading beyond the bounds of the pointer will crash the program or cause * undefined behavior. Use with care! * */ export function toBuffer( ptr: number, byteOffset?: number, byteLength?: number, ): Buffer; /** * Read a pointer as an {@link ArrayBuffer} * * If `byteLength` is not provided, the pointer is assumed to be 0-terminated. * * @param ptr The memory address to read * @param byteOffset bytes to skip before reading * @param byteLength bytes to read * * While there are some checks to catch invalid pointers, this is a difficult * thing to do safely. Passing an invalid pointer can crash the program and * reading beyond the bounds of the pointer will crash the program or cause * undefined behavior. Use with care! */ export function toArrayBuffer( ptr: number, byteOffset?: number, byteLength?: number, ): ArrayBuffer; /** * Get the pointer backing a {@link TypedArray} or {@link ArrayBuffer} * * Use this to pass {@link TypedArray} or {@link ArrayBuffer} to C functions. * * This is for use with FFI functions. For performance reasons, FFI will * not automatically convert typed arrays to C pointers. * * @param {TypedArray|ArrayBuffer|DataView} view the typed array or array buffer to get the pointer for * @param {number} byteOffset optional offset into the view in bytes * * @example * * From JavaScript: * ```js * const array = new Uint8Array(10); * const rawPtr = ptr(array); * myFFIFunction(rawPtr); * ``` * To C: * ```c * void myFFIFunction(char* rawPtr) { * // Do something with rawPtr * } * ``` * */ export function ptr( view: TypedArray | ArrayBufferLike | DataView, byteOffset?: number, ): number; /** * Get a string from a UTF-8 encoded C string * If `byteLength` is not provided, the string is assumed to be null-terminated. * * @example * ```js * var ptr = lib.symbols.getVersion(); * console.log(new CString(ptr)); * ``` * * @example * ```js * var ptr = lib.symbols.getVersion(); * // print the first 4 characters * console.log(new CString(ptr, 0, 4)); * ``` * * While there are some checks to catch invalid pointers, this is a difficult * thing to do safely. Passing an invalid pointer can crash the program and * reading beyond the bounds of the pointer will crash the program or cause * undefined behavior. Use with care! */ export class CString extends String { /** * Get a string from a UTF-8 encoded C string * If `byteLength` is not provided, the string is assumed to be null-terminated. * * @param ptr The pointer to the C string * @param byteOffset bytes to skip before reading * @param byteLength bytes to read * * * @example * ```js * var ptr = lib.symbols.getVersion(); * console.log(new CString(ptr)); * ``` * * @example * ```js * var ptr = lib.symbols.getVersion(); * // print the first 4 characters * console.log(new CString(ptr, 0, 4)); * ``` * * While there are some checks to catch invalid pointers, this is a difficult * thing to do safely. Passing an invalid pointer can crash the program and * reading beyond the bounds of the pointer will crash the program or cause * undefined behavior. Use with care! */ constructor(ptr: number, byteOffset?: number, byteLength?: number); /** * The ptr to the C string * * This `CString` instance is a clone of the string, so it * is safe to continue using this instance after the `ptr` has been * freed. */ ptr: number; byteOffset?: number; byteLength?: number; /** * Get the {@link ptr} as an `ArrayBuffer` * * `null` or empty ptrs returns an `ArrayBuffer` with `byteLength` 0 */ get arrayBuffer(): ArrayBuffer; } /** * Pass a JavaScript function to FFI (Foreign Function Interface) */ export class JSCallback { /** * Enable a JavaScript callback function to be passed to C with bun:ffi * * @param callback The JavaScript function to be called * @param definition The C function definition */ constructor(callback: (...args: any[]) => any, definition: FFIFunction); /** * The pointer to the C function * * Becomes `null` once {@link JSCallback.prototype.close} is called */ readonly ptr: number | null; /** * Can the callback be called from a different thread? */ readonly threadsafe: boolean; /** * Free the memory allocated for the callback * * If called multiple times, does nothing after the first call. */ close(): void; } /** * View the generated C code for FFI bindings * * You probably won't need this unless there's a bug in the FFI bindings * generator or you're just curious. */ export function viewSource(symbols: Symbols, is_callback?: false): string[]; export function viewSource(callback: FFIFunction, is_callback: true): string; /** * Platform-specific file extension name for dynamic libraries * * "." is not included * * @example * ```js * "dylib" // macOS * ``` * * @example * ```js * "so" // linux * ``` */ export const suffix: string; }