From 59d91f755f440b5db7dc2e553d3722ca2960da58 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 6 Sep 2023 09:13:29 -0300 Subject: [PATCH] feat: add types and update ref-napi Signed-off-by: Ariel Gentile --- package-lock.json | 8 +- package.json | 2 +- types/index.d.ts | 517 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 522 insertions(+), 5 deletions(-) create mode 100644 types/index.d.ts diff --git a/package-lock.json b/package-lock.json index 96a98a04..1a639be3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@2060.io/ref-napi": "3.0.4", + "@2060.io/ref-napi": "3.0.5", "debug": "^4.1.1", "get-uv-event-loop-napi-h": "^1.0.5", "node-addon-api": "^3.0.0", @@ -31,9 +31,9 @@ } }, "node_modules/@2060.io/ref-napi": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@2060.io/ref-napi/-/ref-napi-3.0.4.tgz", - "integrity": "sha512-Aqf699E+DKs2xANx8bSkuzXyG9gcZ2iFkAk1kUTA8KbV5BSPQtIcBJexzohSRi9QWDhP4X54NLm7xwGA0UNCdQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@2060.io/ref-napi/-/ref-napi-3.0.5.tgz", + "integrity": "sha512-rLtrYxiE1R9mg53kE2HLiTCu4LxveeU5r5XoFX+fvMVj4XqlJQtPL47AZCUYQAf4ARZhM0j1SBwgIV77FNllEQ==", "hasInstallScript": true, "dependencies": { "debug": "^4.1.1", diff --git a/package.json b/package.json index c6c3fb5f..4b52a64c 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "get-uv-event-loop-napi-h": "^1.0.5", "node-addon-api": "^3.0.0", "node-gyp-build": "^4.2.1", - "@2060.io/ref-napi": "3.0.4", + "@2060.io/ref-napi": "3.0.5", "ref-struct-di": "^1.1.0" }, "devDependencies": { diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 00000000..a7ed1dad --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,517 @@ +// Type definitions for ffi-napi 4.0 +// Project: https://github.com/node-ffi-napi/node-ffi-napi +// Definitions by: Keerthi Niranjan , Kiran Niranjan +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +import ref = require('@2060.io/ref-napi'); +import ref_struct = require('ref-struct-di'); +import StructType = ref_struct.StructType; + +/** + * This is a marker type that causes TypeScript to use string literal inference when inferring a generic from an array of {@link ref.TypeLike}. + */ +export type ArgTypesInferenceMarker = ["void"]; + +export interface LibraryFunctionOptions { + abi?: number | undefined; + async?: boolean | undefined; + varargs?: boolean | undefined; +} + +/** + * Base constraint for an object-based library type definition. + */ +export type LibraryObjectDefinitionBase = Record; + +/** + * This is a marker type that causes TypeScript to use string literal inference when inferring a generic from {@link LibraryObjectDefinitionBase}. + * If it is not used, `new Library(..., { x: ["int", ["int"]] })` will be inferred as `new Library<{ x: [string, string[]] }>(...)` instead of `new UnionType<{ x: ["int", ["int"]] }>(...)`. + */ +export type LibraryObjectDefinitionInferenceMarker = Record; + +/** + * Converts a {@link LibraryObjectDefinitionBase} into a consistent subtype of {@link LibraryDefinitionBase}. If `any` is used, it is passed along + * to be interpreted to use a fallback definition for a union. + */ +export type LibraryObjectDefinitionToLibraryDefinition = + [T] extends [never] | [0] ? any : // catches T extends never/any (since `0` doesn't overlap with our constraint) + { [P in keyof T]: [retType: ref.CoerceType, argTypes: ref.CoerceTypes, opts: T[P][2]] }; + +/** + * Base constraint for a consistent library type definition. + */ +export type LibraryDefinitionBase = Record; + +/** + * Represents a {@link Library} instance created from a {@link LibraryDefinitionBase}. + */ +export type LibraryObject = + [T] extends [never] | [0] ? any : // catches T extends never/any (since `0` doesn't overlap with our constraint) + { + [P in keyof T]: + T[P][2] extends undefined ? ForeignFunction, ref.UnderlyingTypes> : + T[P][2] extends { varargs: true } ? VariadicForeignFunction : + T[P][2] extends { async: true } ? ForeignFunction, ref.UnderlyingTypes>["async"] : + ForeignFunction, ref.UnderlyingTypes>; + }; + +/** Provides a friendly API on-top of `DynamicLibrary` and `ForeignFunction`. */ +export interface Library { + /** The extension to use on libraries. */ + EXT: string; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + new ( + libFile: string | null, + funcs: TDefinition, + lib: T + ): T & LibraryObject>; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + new ( + libFile: string | null, + funcs: TDefinition, + lib?: object + ): LibraryObject>; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + new (libFile: string | null, funcs?: Record, lib?: object): any; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + ( + libFile: string | null, + funcs: TDefinition, + lib: T + ): T & LibraryObject>; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + ( + libFile: string | null, + funcs: TDefinition, + lib?: object + ): LibraryObject>; + + /** + * @param libFile name of library + * @param funcs hash of [retType, [...argType], opts?: {abi?, async?, varargs?}] + * @param lib hash that will be extended + */ + (libFile: string | null, funcs?: Record, lib?: object): any; +} +export const Library: Library; + +/** Get value of errno. */ +export function errno(): number; + +export interface Function< + TReturnType extends ref.Type = ref.Type, + TArgTypes extends ref.Type[] = ref.Type[] +> extends ref.Type<(...args: ref.UnderlyingTypes) => ref.UnderlyingType> { + /** The type of return value. */ + retType: TReturnType; + /** The type of arguments. */ + argTypes: TArgTypes; + /** Is set for node-ffi functions. */ + ffi_type: PFFI_TYPE; + abi: number; + + /** Get a `Callback` pointer of this function type. */ + toPointer(fn: (...args: ref.UnderlyingTypes) => ref.UnderlyingType): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + /** Get a `ForeignFunction` of this function type. */ + toFunction(buf: Buffer): ForeignFunction, ref.UnderlyingTypes>; +} + +/** Creates and returns a type for a C function pointer. */ +export const Function: { + // NOTE: This overload is a subtype of the next overload, but provides better completions. + new ( + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): Function, ref.CoerceTypes>; + new ( + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): Function, ref.CoerceTypes>; + + new (retType: ref.TypeLike, argTypes: ref.TypeLike[], abi?: number): Function; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + ( + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): Function, ref.CoerceTypes>; + ( + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): Function, ref.CoerceTypes>; + + (retType: ref.TypeLike, argTypes: ref.TypeLike[], abi?: number): Function; +}; + +export interface ForeignFunction { + (...args: TArgs): TReturn; + async(...args: [...TArgs, (err: any, value: TReturn) => void]): void; +} + +/** + * Represents a foreign function in another library. Manages all of the aspects + * of function execution, including marshalling the data parameters for the + * function into native types and also unmarshalling the return from function + * execution. + */ +export const ForeignFunction: { + // NOTE: This overload is a subtype of the next overload, but provides better completions. + new ( + ptr: Buffer, + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): ForeignFunction, ref.UnderlyingTypes>; + new ( + ptr: Buffer, + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): ForeignFunction, ref.UnderlyingTypes>; + + new (ptr: Buffer, retType: ref.TypeLike, argTypes: ref.TypeLike[], abi?: number): ForeignFunction; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + ( + ptr: Buffer, + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): ForeignFunction, ref.UnderlyingTypes>; + ( + ptr: Buffer, + retType: TReturnType, + argTypes: TArgTypes, + abi?: number + ): ForeignFunction, ref.UnderlyingTypes>; + + (ptr: Buffer, retType: ref.TypeLike, argTypes: ref.TypeLike[], abi?: number): ForeignFunction; +}; + +export interface VariadicForeignFunction { + /** + * What gets returned is another function that needs to be invoked with the rest + * of the variadic types that are being invoked from the function. + */ + // NOTE: This overload is a subtype of the next overload, but provides better completions. + (...args: A): ForeignFunction, [...ref.UnderlyingTypes, ...ref.UnderlyingTypes]>; + /** + * What gets returned is another function that needs to be invoked with the rest + * of the variadic types that are being invoked from the function. + */ + (...args: A): ForeignFunction, [...ref.UnderlyingTypes, ...ref.UnderlyingTypes]>; + + /** + * Return type as a property of the function generator to + * allow for monkey patching the return value in the very rare case where the + * return type is variadic as well + */ + returnType: TReturnType; +} + +/** + * For when you want to call to a C function with variable amount of arguments. + * i.e. `printf`. + * + * This function takes care of caching and reusing `ForeignFunction` instances that + * contain the same ffi_type argument signature. + */ +export const VariadicForeignFunction: { + // NOTE: This overload is a subtype of the next overload, but provides better completions. + new ( + ptr: Buffer, + ret: TReturnType, + fixedArgs: TArgTypes, + abi?: number + ): VariadicForeignFunction, ref.CoerceTypes>; + new ( + ptr: Buffer, + ret: TReturnType, + fixedArgs: TArgTypes, + abi?: number + ): VariadicForeignFunction, ref.CoerceTypes>; + + new (ptr: Buffer, ret: ref.TypeLike, fixedArgs: ref.TypeLike[], abi?: number): VariadicForeignFunction; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + ( + ptr: Buffer, + ret: TReturnType, + fixedArgs: TArgTypes, + abi?: number + ): VariadicForeignFunction, ref.CoerceTypes>; + ( + ptr: Buffer, + ret: TReturnType, + fixedArgs: TArgTypes, + abi?: number + ): VariadicForeignFunction, ref.CoerceTypes>; + + (ptr: Buffer, ret: ref.TypeLike, fixedArgs: ref.TypeLike[], abi?: number): VariadicForeignFunction; +}; + +/** + * This class loads and fetches function pointers for dynamic libraries + * (`.so`, `.dylib`, `etc`). After the libray's function pointer is acquired, then you + * call {@link DynamicLibrary.get} to retreive a pointer to an exported symbol. You need to + * call `get___` on the pointer to dereference it into its actual value, or + * turn the pointer into a callable function with {@link ForeignFunction}. + */ +export class DynamicLibrary { + /** + * @param mode One of the numeric {@link DynamicLibrary.FLAGS} values. + */ + constructor(path?: string, mode?: number); + + /** + * Close library, returns the result of the `dlclose` system function. + */ + close(): number; + + /** + * Get a symbol from this library. + */ + get(symbol: string): Buffer; + + /** + * Get the result of the `dlerror` system function. + */ + error(): string; + + /** + * Returns the path originally passed to the constructor. + */ + path(): string; +} + +/** + * This class loads and fetches function pointers for dynamic libraries + * (`.so`, `.dylib`, etc). After the libray's function pointer is acquired, then you + * call {@link DynamicLibrary.get} to retreive a pointer to an exported symbol. You need to + * call `get___` on the pointer to dereference it into its actual value, or + * turn the pointer into a callable function with {@link ForeignFunction}. + * + * @param mode One of the numeric {@link DynamicLibrary.FLAGS} values. + */ +export function DynamicLibrary(path?: string, mode?: number): DynamicLibrary; +export namespace DynamicLibrary { + /** + * Exported flags from "dlfcn.h" + */ + namespace FLAGS { + // flags for dlopen() + const RTLD_LAZY: number; + const RTLD_NOW: number; + const RTLD_LOCAL: number; + const RTLD_GLOBAL: number; + const RTLD_NOLOAD: number | undefined; // not defined on Windows + const RTLD_NODELETE: number | undefined; // not defined on Windows + const RTLD_FIRST: number | undefined; // not defined on Windows + + // flags for dlsym() + const RTLD_NEXT: ref.Pointer; + const RTLD_DEFAULT: ref.Pointer; + const RTLD_SELF: ref.Pointer | undefined; // not defined on Windows + const RTLD_MAIN_ONLY: ref.Pointer | undefined; // not defined on Windows + } +} + +/** + * Turns a JavaScript function into a C function pointer. + * The function pointer may be used in other C functions that + * accept C callback functions. + */ +export interface Callback { + // NOTE: This overload is a subtype of the next overload, but provides better completions. + new ( + retType: TReturnType, + argTypes: TArgTypes, + abi: number, + fn: (...args: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + new ( + retType: TReturnType, + argTypes: TArgTypes, + abi: number, + fn: (...args: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + new ( + retType: TReturnType, + argTypes: TArgTypes, + fn: (...ags: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + new ( + retType: TReturnType, + argTypes: TArgTypes, + fn: (...ags: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + + new (retType: ref.TypeLike, argTypes: ref.TypeLike[], abi: number, fn: (...args: any[]) => any): Buffer; + new (retType: ref.TypeLike, argTypes: ref.TypeLike[], fn: (...args: any[]) => any): Buffer; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + ( + retType: TReturnType, + argTypes: TArgTypes, + abi: number, + fn: (...args: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + ( + retType: TReturnType, + argTypes: TArgTypes, + abi: number, + fn: (...args: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + + // NOTE: This overload is a subtype of the next overload, but provides better completions. + ( + retType: TReturnType, + argTypes: TArgTypes, + fn: (...ags: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + ( + retType: TReturnType, + argTypes: TArgTypes, + fn: (...ags: ref.UnderlyingTypes) => ref.UnderlyingType + ): ref.Pointer<(...args: ref.UnderlyingTypes) => ref.UnderlyingType>; + + (retType: ref.TypeLike, argTypes: ref.TypeLike[], abi: number, fn: (...args: any[]) => any): Buffer; + (retType: ref.TypeLike, argTypes: ref.TypeLike[], fn: (...args: any[]) => any): Buffer; +} +export const Callback: Callback; + +/** Get a `ffi_type *` Buffer appropriate for the given type. */ +export function ffiType(type: ref.TypeLike): ref.Pointer; +export namespace ffiType { + /** + * Define the `ffi_type` struct (see deps/libffi/include/ffi.h) for use in JS. + * This struct type is used internally to define custom struct ret/arg types. + */ + const FFI_TYPE: StructType<{ + size: typeof ref.types.size_t, + alignment: typeof ref.types.ushort, + type: typeof ref.types.ushort, + elements: ref.Type>, + }>; + + type FFI_TYPE = ReturnType; +} + +export function CIF(retType: ref.TypeLike, types: ref.TypeLike[], abi?: number): Buffer; +export function CIF_var(retType: ref.TypeLike, types: ref.TypeLike[], numFixedArgs: number, abi?: number): Buffer; + +export const HAS_OBJC: boolean; + +/** + * Represents an opaque pointer to an {@link FFI_TYPE} struct used for marshalling types to native code. + */ +export type PFFI_TYPE = ref.Pointer; + +declare module "ref-napi" { + interface Type { + /** + * Determines the FFI_TYPE set for a type. + */ + ffi_type?: PFFI_TYPE | undefined; + } +} + +export interface FfiTypesRegistry { + void: PFFI_TYPE; + uint8: PFFI_TYPE; + int8: PFFI_TYPE; + uint16: PFFI_TYPE; + int16: PFFI_TYPE; + uint32: PFFI_TYPE; + int32: PFFI_TYPE; + uint64: PFFI_TYPE; + int64: PFFI_TYPE; + uchar: PFFI_TYPE; + char: PFFI_TYPE; + ushort: PFFI_TYPE; + short: PFFI_TYPE; + uint: PFFI_TYPE; + int: PFFI_TYPE; + float: PFFI_TYPE; + double: PFFI_TYPE; + pointer: PFFI_TYPE; + ulonglong: PFFI_TYPE; + longlong: PFFI_TYPE; + ulong: PFFI_TYPE; + long: PFFI_TYPE; +} + +export const FFI_TYPES: FfiTypesRegistry; +export const FFI_OK: number; +export const FFI_BAD_TYPEDEF: number; +export const FFI_BAD_ABI: number; +export const FFI_DEFAULT_ABI: number; +export const FFI_FIRST_ABI: number; +export const FFI_LAST_ABI: number; + +// NOTE: These are defined depending on the platform ffi-napi was compiled on: +export const FFI_SYSV: number | undefined; // __arm__, Intel x86 Win32, __aarch64__, Intel x86 and AMD x86/x64 +export const FFI_VFP: number | undefined; // __arm__ +export const FFI_UNIX64: number | undefined; // All unix variants +export const FFI_STDCALL: number | undefined; // Intel x86 Win32 +export const FFI_THISCALL: number | undefined; // Intel x86 Win32 +export const FFI_FASTCALL: number | undefined; // Intel x86 Win32 +export const FFI_MS_CDECL: number | undefined; // Intel x86 Win32 +export const FFI_WIN64: number | undefined; // Intel x86 Win64 + +export import RTLD_LAZY = DynamicLibrary.FLAGS.RTLD_LAZY; +export import RTLD_NOW = DynamicLibrary.FLAGS.RTLD_NOW; +export import RTLD_LOCAL = DynamicLibrary.FLAGS.RTLD_LOCAL; +export import RTLD_GLOBAL = DynamicLibrary.FLAGS.RTLD_GLOBAL; +export import RTLD_NOLOAD = DynamicLibrary.FLAGS.RTLD_NOLOAD; +export import RTLD_NODELETE = DynamicLibrary.FLAGS.RTLD_NODELETE; +export import RTLD_FIRST = DynamicLibrary.FLAGS.RTLD_FIRST; +export import RTLD_NEXT = DynamicLibrary.FLAGS.RTLD_NEXT; +export import RTLD_DEFAULT = DynamicLibrary.FLAGS.RTLD_DEFAULT; +export import RTLD_SELF = DynamicLibrary.FLAGS.RTLD_SELF; +export import RTLD_MAIN_ONLY = DynamicLibrary.FLAGS.RTLD_MAIN_ONLY; + +export const FFI_ARG_SIZE: number; +export const FFI_SARG_SIZE: number; +export const FFI_TYPE_SIZE: number; +export const FFI_CIF_SIZE: number; + +export const LIB_EXT: string; + +export import FFI_TYPE = ffiType.FFI_TYPE; + +/** Default types. */ +export import types = ref.types; \ No newline at end of file