@txikijs/types
Version:
TypeScript types for the txiki.js runtime
272 lines (242 loc) • 9.79 kB
TypeScript
/**
* 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>;
}