UNPKG

@txikijs/types

Version:
272 lines (242 loc) 9.79 kB
/** * Foreign Function Interface module. * * Call native C library functions directly from JavaScript. Supports loading * shared libraries, defining function signatures, and working with C types * including structs, pointers, and callbacks. * * ```js * import { Lib, CFunction, types } from 'tjs:ffi'; * * const lib = new Lib(Lib.LIBC_NAME); * const getpid = new CFunction(lib.symbol('getpid'), types.sint, []); * console.log(`PID: ${getpid.call()}`); * ``` * * @module tjs:ffi */ declare module 'tjs:ffi'{ /** * Opaque pointer object. Stores a native `void*` with full precision. * Null pointers are represented as JavaScript `null`. */ export interface NativePointer { /** Returns hex string representation, e.g. `"0x7fff5a2b3c00"`. */ toString(): string; /** Returns a new pointer offset by `n` bytes. */ offset(n: number): NativePointer; /** Returns `true` if both pointers refer to the same address. */ equals(other: NativePointer | null): boolean; } /** * Direct memory reads from a pointer at a given byte offset. * Faster than creating an intermediate buffer for one-off reads. */ export const read: { u8(ptr: NativePointer, offset?: number): number; i8(ptr: NativePointer, offset?: number): number; u16(ptr: NativePointer, offset?: number): number; i16(ptr: NativePointer, offset?: number): number; u32(ptr: NativePointer, offset?: number): number; i32(ptr: NativePointer, offset?: number): number; u64(ptr: NativePointer, offset?: number): number; i64(ptr: NativePointer, offset?: number): number; f32(ptr: NativePointer, offset?: number): number; f64(ptr: NativePointer, offset?: number): number; ptr(ptr: NativePointer, offset?: number): NativePointer | null; }; export class DlSymbol{ readonly addr: NativePointer; } interface SimpleType<T = any>{ toBuffer(data: T, ctx?: {}): Uint8Array; fromBuffer(buffer: Uint8Array, ctx?: {}): T; readonly size: number; readonly name: string; } export class AdvancedType<T, ST extends SimpleType<T>> implements SimpleType<T>{ constructor(type: ST, conf: { toBuffer?: (data: T, ctx?: {}) => Uint8Array, fromBuffer?: (buf: Uint8Array, ctx?: {}) => T, getFfiTypeStruct?: () => SimpleType<T> }); readonly ffiType: ST; readonly ffiTypeStruct: SimpleType<T> toBuffer(data: T, ctx?: {}): Uint8Array; fromBuffer(buffer: Uint8Array, ctx?: {}): T; readonly size: number; readonly name: string; } export class Lib{ constructor(libname: string); symbol(name: string): DlSymbol; /** * Explicitly close the shared library handle. After calling this, * any symbols obtained from this library must not be used. * * Aliased as `Symbol.dispose`, so `using lib = new Lib(...)` closes * the handle at scope exit. */ close(): void; static LIBC_NAME: string; static LIBM_NAME: string; registerType(name: string, type: SimpleType): void; getType(name: string): undefined|SimpleType; registerFunction(name: string, func: CFunction): void; getFunc(name: string): CFunction; call(funcname: string, ...args: any[]): any; parseCProto(header: string): void; } export interface Lib extends Disposable {} export class CFunction<JRT = any, JAT extends Array<any> = any[]>{ //TODO: better typing mechanism for Arg types constructor(symbol: DlSymbol, rtype: SimpleType<JRT>, argtypes: SimpleType[], fixed?: number); call(...argsJs: JAT): JRT; } export const types: { void: SimpleType<void>, uint8: SimpleType<number>, sint8: SimpleType<number>, uint16: SimpleType<number>, sint16: SimpleType<number>, uint32: SimpleType<number>, sint32: SimpleType<number>, uint64: SimpleType<number>, sint64: SimpleType<number>, float: SimpleType<number>, double: SimpleType<number>, pointer: SimpleType<NativePointer>, longdouble: SimpleType<number>, uchar: SimpleType<number>, schar: SimpleType<number>, ushort: SimpleType<number>, sshort: SimpleType<number>, uint: SimpleType<number>, sint: SimpleType<number>, ulong: SimpleType<number>, slong: SimpleType<number>, sllong: SimpleType<number>, ullong: SimpleType<number>, size: SimpleType<number>, ssize: SimpleType<number>, string: SimpleType<string> buffer: SimpleType<Uint8Array> jscallback: SimpleType<(...args: any)=>any> } /** * Platform-specific shared library file extension: `'dylib'` on macOS, * `'so'` on Linux, `'dll'` on Windows. */ export const suffix: string; export function bufferToString(buf: Uint8Array): string; export function stringToBuffer(s: string): Uint8Array; export function bufferToPointer(buf: Uint8Array): NativePointer; export class Pointer<T, N extends number>{ constructor(addr: NativePointer, level: N, type: SimpleType<T>); readonly addr: NativePointer; readonly level: N; readonly type: T; readonly isNull: boolean; deref(): N extends 1 ? T : Pointer<T, any>; derefAll(): T; static createRef<T>(type: SimpleType<T>, data: T): Pointer<T, 1>; static createRefFromBuf<T>(type: SimpleType<T>, buf: Uint8Array): Pointer<T, 1>; } export class PointerType<T, ST extends SimpleType<T>, N extends number> extends AdvancedType<Pointer<T, N>, PointerType<T, ST, N>>{ constructor(type: ST , level: N); toBuffer(data: Pointer<T, N>|NativePointer, ctx?: {}): Uint8Array; fromBuffer(buf: Uint8Array, ctx?: {}): Pointer<T, N>; get type(): ST; get level(): N; } export class StructType<Obj, FT extends Array<({ [K in keyof Obj]: [K, SimpleType<Obj[K]>] })[keyof Obj]>> extends AdvancedType<Obj, StructType<Obj, FT>>{ constructor(fields: FT, name: string); readonly fields: FT; } export class ArrayType<T> extends AdvancedType<Array<T>, ArrayType<T>>{ constructor(type: SimpleType<T>, length: number, name: string); readonly ffiTypeStruct: SimpleType<Array<T>>; readonly length: number readonly size: number; } export class StaticStringType extends AdvancedType<string, StaticStringType>{ constructor(length: number, name: string); toBuffer(str: string, ctx?: {}): Uint8Array; fromBuffer(buf: Uint8Array, ctx?: {}): string; } export function errno(): number; export function strerror(err?: number): string; export class JSCallback<RT, AT extends []>{ //TODO: better typing mechanism for Arg types constructor(rtype: SimpleType<RT>, argtypes: Array<SimpleType<AT[0]>>, func: (...args: AT)=>RT); readonly addr: NativePointer; } /** * String aliases for FFI types. Can be used in {@link dlopen} symbol definitions * instead of type objects from {@link types}. * * Supports short (`i32`, `u8`, `f64`, `ptr`), C-style (`int`, `char`, `double`), * and stdint-style (`uint32_t`, `int64_t`) names. */ export type TypeAlias = | 'void' | 'u8' | 'uint8' | 'uint8_t' | 'i8' | 'sint8' | 'int8_t' | 'u16' | 'uint16' | 'uint16_t' | 'i16' | 'sint16' | 'int16_t' | 'u32' | 'uint32' | 'uint32_t' | 'int' | 'i32' | 'sint32' | 'int32_t' | 'u64' | 'uint64' | 'uint64_t' | 'i64' | 'sint64' | 'int64_t' | 'f32' | 'float' | 'f64' | 'double' | 'pointer' | 'ptr' | 'string' | 'cstring' | 'buffer' | 'uchar' | 'schar' | 'char' | 'ushort' | 'sshort' | 'uint' | 'sint' | 'ulong' | 'slong' | 'long' | 'size_t' | 'ssize_t'; export type TypeOrAlias = SimpleType | TypeAlias; /** * Describes a native function symbol for use with {@link dlopen}. */ export interface DlopenSymbol { /** Argument types. Defaults to `[]` (no arguments) if omitted. */ args?: TypeOrAlias[]; /** Return type. Defaults to `'void'` if omitted. */ returns?: TypeOrAlias; /** Number of fixed arguments for variadic functions. */ fixed?: number; } export interface DlopenResult<T extends Record<string, DlopenSymbol>> { /** Object containing callable functions for each declared symbol. */ symbols: { [K in keyof T]: (...args: any[]) => any }; /** Close the shared library handle. */ close(): void; } /** * Load a shared library and bind symbols as callable functions. * * Types can be specified as {@link SimpleType} objects or as string aliases * (e.g. `'i32'`, `'string'`, `'ptr'`). * * ```js * import { dlopen } from 'tjs:ffi'; * * const { symbols, close } = dlopen('./libfoo.dylib', { * add: { args: ['i32', 'i32'], returns: 'i32' }, * version: { args: [], returns: 'string' }, * }); * * console.log(symbols.add(1, 2)); * console.log(symbols.version()); * close(); * ``` * * @param path - Path to the shared library. * @param symbols - Object mapping symbol names to their type signatures. */ export function dlopen<T extends Record<string, DlopenSymbol>>(path: string, symbols: T): DlopenResult<T>; }