UNPKG

@types/node

Version:
487 lines (486 loc) • 20 kB
declare module "node:ffi" { import { NonSharedBuffer } from "node:buffer"; interface FunctionSignature { return?: ReturnType | undefined; arguments?: readonly ArgumentType[] | undefined; } interface FunctionDefinitions { [symbol: string]: FunctionSignature; } type CallbackFunction<R extends ReturnType = any, P extends readonly ArgumentType[] = any[]> = ( ...args: { [K in keyof P]: ArgumentTypeMap[DataTypeMap[P[K]]] } ) => ReturnTypeMap[DataTypeMap[R]]; interface WrappedFunction<R extends ReturnType = any, P extends readonly ArgumentType[] = any[]> extends CallbackFunction<R, P> { readonly pointer: bigint; } type CallbackFunctionFromSignature<T extends FunctionSignature> = CallbackFunction< ReturnTypeFromFunctionSignature<T>, ArgumentTypesFromFunctionSignature<T> >; type WrappedFunctionFromSignature<T extends FunctionSignature> = WrappedFunction< ReturnTypeFromFunctionSignature<T>, ArgumentTypesFromFunctionSignature<T> >; type WrappedFunctionsFromDefinitions<T extends FunctionDefinitions> = { [K in keyof T]: WrappedFunctionFromSignature<T[K]>; }; type ReturnTypeFromFunctionSignature<T extends FunctionSignature> = "return" extends keyof T ? T extends { return: infer R extends ReturnType } ? R : any : "void"; type ArgumentTypesFromFunctionSignature<T extends FunctionSignature> = "arguments" extends keyof T ? T extends { arguments: infer P extends readonly ArgumentType[] } ? P : any[] : []; interface DynamicLibraryResult<T extends FunctionDefinitions> extends Disposable { lib: DynamicLibrary; functions: WrappedFunctionsFromDefinitions<T>; } /** * The native shared library suffix for the current platform: * * * `'dylib'` on macOS * * `'so'` on Unix-like platforms * * `'dll'` on Windows * * This can be used to build portable library paths: * * ```js * const { suffix } = require('node:ffi'); * * const path = `libsqlite3.${suffix}`; * ``` * @since v26.1.0 */ const suffix: string; /** * Loads a dynamic library and resolves the requested function definitions. * * On Windows passing `null` is not supported. * * When `definitions` is omitted, `functions` is returned as an empty object until * symbols are resolved explicitly. * * The returned object also implements the explicit resource management protocol, * so it can be used with the `using` declaration. Disposing the returned * object closes the library handle. * * ```js * import { dlopen } from 'node:ffi'; * * { * using handle = dlopen('./mylib.so', { * add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, * }); * console.log(handle.functions.add_i32(20, 22)); * } // handle.lib.close() is invoked automatically here. * ``` * * ```js * import { dlopen } from 'node:ffi'; * * const { lib, functions } = dlopen('./mylib.so', { * add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, * string_length: { arguments: ['pointer'], return: 'u64' }, * }); * * console.log(functions.add_i32(20, 22)); * ``` * @since v26.1.0 * @param path Path to a dynamic library, or `null` to resolve symbols * from the current process image. * @param definitions Symbol definitions to resolve immediately. */ function dlopen<const T extends FunctionDefinitions = {}>( path: string | null, definitions?: T, ): DynamicLibraryResult<T>; /** * Closes a dynamic library. * * This is equivalent to calling `handle.close()`. * @since v26.1.0 */ function dlclose(handle: DynamicLibrary): void; /** * Resolves a symbol address from a loaded library. * * This is equivalent to calling `handle.getSymbol(symbol)`. * @since v26.1.0 */ function dlsym(handle: DynamicLibrary, symbol: string): bigint; /** * @since v26.1.0 */ class DynamicLibrary { /** * Loads the dynamic library without resolving any functions eagerly. * * On Windows passing `null` is not supported. * * ```js * const { DynamicLibrary } = require('node:ffi'); * * const lib = new DynamicLibrary('./mylib.so'); * ``` * @param path Path to a dynamic library, or `null` to resolve symbols * from the current process image. */ constructor(path: string | null); /** * The path used to load the library. */ readonly path: string; /** * An object containing previously resolved symbol addresses as `bigint` values. */ readonly symbols: { [symbol: string]: bigint }; /** * Closes the library handle. * * `DynamicLibrary` implements the explicit resource management protocol, so a * library instance can be managed with the `using` declaration. Leaving the * enclosing scope invokes `library.close()` automatically. * * ```js * import { DynamicLibrary } from 'node:ffi'; * * { * using lib = new DynamicLibrary('./mylib.so'); * // Use `lib` here; `lib.close()` is called when the block exits. * } * ``` * * Calling `library.close()` (or disposing the library) more than once is a no-op. * * After a library has been closed: * * * Resolved function wrappers become invalid. * * Further symbol and function resolution throws. * * Registered callbacks are invalidated. * * Closing a library does not make previously exported callback pointers safe to * reuse. Node.js does not track or revoke callback pointers that have already * been handed to native code. * * If native code still holds a callback pointer after `library.close()` or after * `library.unregisterCallback(pointer)`, invoking that pointer has undefined * behavior, is not allowed, and is dangerous: it can crash the process, produce * incorrect output, or corrupt memory. Native code must stop using callback * addresses before the library is closed or before the callback is unregistered. * * Calling `library.close()` from one of the library's active callbacks is * unsupported and dangerous. The callback must return before the library is * closed. */ close(): void; /** * Calls `library.close()`. This allows `DynamicLibrary` instances to be used with * the `using` declaration for automatic cleanup when the enclosing scope * exits. It is a no-op on a library that has already been closed. * @since v26.1.0 */ [Symbol.dispose](): void; /** * Resolves a symbol and returns a callable JavaScript wrapper. * * The returned function has a `.pointer` property containing the native function * address as a `bigint`. * * If the same symbol has already been resolved, requesting it again with a * different signature throws. * * ```js * const { DynamicLibrary } = require('node:ffi'); * * const lib = new DynamicLibrary('./mylib.so'); * const add = lib.getFunction('add_i32', { * arguments: ['i32', 'i32'], * return: 'i32', * }); * * console.log(add(20, 22)); * console.log(add.pointer); * ``` */ getFunction<const T extends FunctionSignature>(name: string, signature: T): WrappedFunctionFromSignature<T>; /** * When `definitions` is provided, resolves each named symbol and returns an * object containing callable wrappers. * * When `definitions` is omitted, returns wrappers for all functions that have * already been resolved on the library. */ getFunctions(): { [symbol: string]: WrappedFunction }; getFunctions<const T extends FunctionDefinitions>(definitions: T): WrappedFunctionsFromDefinitions<T>; /** * Resolves a symbol and returns its native address as a `bigint`. */ getSymbol(name: string): bigint; /** * Returns an object containing all previously resolved symbol addresses. */ getSymbols(): Record<string, bigint>; /** * Creates a native callback pointer backed by a JavaScript function. * * When `signature` is omitted, the callback uses a default `void ()` signature. * * The return value is the callback pointer address as a `bigint`. It can be * passed to native functions expecting a callback pointer. * * ```js * const { DynamicLibrary } = require('node:ffi'); * * const lib = new DynamicLibrary('./mylib.so'); * * const callback = lib.registerCallback( * { arguments: ['i32'], return: 'i32' }, * (value) => value * 2, * ); * ``` * * Callbacks are subject to the following restrictions: * * * They must be invoked on the same system thread where they were created. * * They must not throw exceptions. * * They must not return promises. * * They must return a value compatible with the declared return type. * * They must not call `library.close()` on their owning library while running. * * They must not unregister themselves while running. * * Closing the owning library or unregistering the currently executing callback * from inside the callback is unsupported and dangerous. Doing so may crash the * process, produce incorrect output, or corrupt memory. */ registerCallback(callback: () => void): bigint; registerCallback<const T extends FunctionSignature>( signature: T, callback: CallbackFunctionFromSignature<T>, ): bigint; /** * Releases a callback previously created with `library.registerCallback()`. * * Calling `library.unregisterCallback(pointer)` for a callback that is currently * executing is unsupported and dangerous. The callback must return before it is * unregistered. * * After `library.unregisterCallback(pointer)` returns, invoking that callback * pointer from native code has undefined behavior, is not allowed, and is * dangerous: it can crash the process, produce incorrect output, or corrupt * memory. */ unregisterCallback(pointer: bigint): void; /** * Keeps the callback strongly referenced by JavaScript. */ refCallback(pointer: bigint): void; /** * Allows the callback to become weakly referenced by JavaScript. * * If the callback function is later garbage collected, subsequent native * invocations become a no-op. Non-void return values are zero-initialized before * returning to native code. */ unrefCallback(pointer: bigint): void; } function getInt8(pointer: bigint, offset?: number): number; function getUint8(pointer: bigint, offset?: number): number; function getInt16(pointer: bigint, offset?: number): number; function getUint16(pointer: bigint, offset?: number): number; function getInt32(pointer: bigint, offset?: number): number; function getUint32(pointer: bigint, offset?: number): number; function getInt64(pointer: bigint, offset?: number): bigint; function getUint64(pointer: bigint, offset?: number): bigint; function getFloat32(pointer: bigint, offset?: number): number; function getFloat64(pointer: bigint, offset?: number): number; function setInt8(pointer: bigint, offset: number, value: number): void; function setUint8(pointer: bigint, offset: number, value: number): void; function setInt16(pointer: bigint, offset: number, value: number): void; function setUint16(pointer: bigint, offset: number, value: number): void; function setInt32(pointer: bigint, offset: number, value: number): void; function setUint32(pointer: bigint, offset: number, value: number): void; function setInt64(pointer: bigint, offset: number, value: number | bigint): void; function setUint64(pointer: bigint, offset: number, value: number | bigint): void; function setFloat32(pointer: bigint, offset: number, value: number): void; function setFloat64(pointer: bigint, offset: number, value: number): void; /** * Reads a NUL-terminated UTF-8 string from native memory. * * If `pointer` is `0n`, `null` is returned. * * This function does not validate that `pointer` refers to readable memory or * that the pointed-to data is terminated with `\0`. Passing an invalid pointer, * a pointer to freed memory, or a pointer to bytes without a terminating NUL can * read unrelated memory, crash the process, or produce truncated or garbled * output. * @since v26.1.0 */ function toString(pointer: bigint): string | null; /** * Creates a `Buffer` from native memory. * * When `copy` is `true`, the returned `Buffer` owns its own copied memory. * When `copy` is `false`, the returned `Buffer` references the original native * memory directly. * * Using `copy: false` is a zero-copy escape hatch. The returned `Buffer` is a * writable view onto foreign memory, so writes in JavaScript update the original * native memory directly. The caller must guarantee that: * * * `pointer` remains valid for the entire lifetime of the returned `Buffer`. * * `length` stays within the allocated native region. * * no native code frees or repurposes that memory while JavaScript still uses * the `Buffer`. * * Memory protection is observed. For example, read-only memory pages must not * be written to. * * If these guarantees are not met, reading or writing the `Buffer` can corrupt * memory or crash the process. * @since v26.1.0 * @param copy When `false`, creates a zero-copy view. **Default:** `true`. */ function toBuffer(pointer: bigint, length: number, copy?: boolean): NonSharedBuffer; /** * Creates an `ArrayBuffer` from native memory. * * When `copy` is `true`, the returned `ArrayBuffer` contains copied bytes. * When `copy` is `false`, the returned `ArrayBuffer` references the original * native memory directly. * * The same lifetime and bounds requirements described for * `ffi.toBuffer(pointer, length, copy)` apply * here. With `copy: false`, the * returned `ArrayBuffer` is a zero-copy view of foreign memory and is only safe * while that memory remains allocated, unchanged in layout, and valid for the * entire exposed range. * @since v26.1.0 * @param copy When `false`, creates a zero-copy view. **Default:** `true`. */ function toArrayBuffer(pointer: bigint, length: number, copy?: boolean): ArrayBuffer; /** * Copies a JavaScript string into native memory and appends a trailing NUL * terminator. * * `length` must be large enough to hold the full encoded string plus the trailing * NUL terminator. For UTF-16 and UCS-2 encodings, the trailing terminator uses * two zero bytes. * * `pointer` must refer to writable native memory with at least `length` bytes of * available storage. This function does not allocate memory on its own. * * `string` must be a JavaScript string. `encoding` must be a string. * @since v26.1.0 * @param encoding **Default:** `'utf8'`. */ function exportString(string: string, pointer: bigint, length: number, encoding?: BufferEncoding): void; /** * Copies bytes from a `Buffer` into native memory. * * `length` must be at least `buffer.length`. * * `pointer` must refer to writable native memory with at least `length` bytes of * available storage. This function does not allocate memory on its own. * * `buffer` must be a Node.js `Buffer`. * @since v26.1.0 */ function exportBuffer(buffer: Buffer, pointer: bigint, length: number): void; /** * Copies bytes from an `ArrayBuffer` into native memory. * * `length` must be at least `arrayBuffer.byteLength`. * * `pointer` must refer to writable native memory with at least `length` bytes of * available storage. This function does not allocate memory on its own. * @since v26.1.0 */ function exportArrayBuffer(arrayBuffer: ArrayBuffer, pointer: bigint, length: number): void; /** * Copies bytes from an `ArrayBufferView` into native memory. * * `length` must be at least `arrayBufferView.byteLength`. * * `pointer` must refer to writable native memory with at least `length` bytes of * available storage. This function does not allocate memory on its own. * @since v26.1.0 */ function exportArrayBufferView(arrayBufferView: NodeJS.ArrayBufferView, pointer: bigint, length: number): void; /** * Returns the raw memory address of JavaScript-managed byte storage. * * This is unsafe and dangerous. The returned pointer can become invalid if the * underlying memory is detached, resized, transferred, or otherwise invalidated. * Using stale pointers can cause memory corruption or process crashes. * @since v26.1.0 */ function getRawPointer(source: ArrayBuffer | NodeJS.ArrayBufferView): bigint; type ReturnType = { [K in keyof DataTypeMap]: K }[keyof DataTypeMap]; type ArgumentType = Exclude<ReturnType, "void">; interface DataTypeMap { void: "void"; char: "number"; bool: "number"; i8: "number"; int8: "number"; u8: "number"; uint8: "number"; i16: "number"; int16: "number"; u16: "number"; uint16: "number"; i32: "number"; int32: "number"; u32: "number"; uint32: "number"; i64: "bigint"; int64: "bigint"; u64: "bigint"; uint64: "bigint"; float: "number"; f32: "number"; double: "number"; f64: "number"; pointer: "pointer"; ptr: "pointer"; function: "pointer"; buffer: "pointer"; arraybuffer: "pointer"; string: "pointer"; str: "pointer"; } interface ArgumentTypeMap { "number": number; "bigint": bigint; "pointer": bigint | string | ArrayBuffer | NodeJS.ArrayBufferView | null; } interface ReturnTypeMap { // eslint-disable-next-line @typescript-eslint/no-invalid-void-type "void": void; "number": number; "bigint": bigint; "pointer": bigint | null; } enum types { VOID = "void", POINTER = "pointer", BUFFER = "buffer", ARRAY_BUFFER = "arraybuffer", FUNCTION = "function", BOOL = "bool", CHAR = "char", STRING = "string", FLOAT = "float", DOUBLE = "double", INT_8 = "int8", UINT_8 = "uint8", INT_16 = "int16", UINT_16 = "uint16", INT_32 = "int32", UINT_32 = "uint32", INT_64 = "int64", UINT_64 = "uint64", FLOAT_32 = "float32", FLOAT_64 = "float64", } }