From 6d31303c4d85130b8dfb5d27e21cb22f4bdc2cef Mon Sep 17 00:00:00 2001 From: Paul Loyd Date: Sat, 7 Sep 2013 00:57:40 +0400 Subject: [PATCH] Add "ffi" module --- node-ffi/node-ffi-tests.ts | 66 ++++++-- node-ffi/node-ffi.d.ts | 305 ++++++++++++++++++++++++++++++------- 2 files changed, 304 insertions(+), 67 deletions(-) diff --git a/node-ffi/node-ffi-tests.ts b/node-ffi/node-ffi-tests.ts index aba1f708d..a29d8dbd6 100644 --- a/node-ffi/node-ffi-tests.ts +++ b/node-ffi/node-ffi-tests.ts @@ -1,18 +1,56 @@ /// +import ffi = require('ffi'); import ref = require('ref'); -ref.address(new Buffer(1)); -var intBuf = ref.alloc(ref.types.int); -var intWith4 = ref.alloc(ref.types.int, 4); -var buf0 = ref.allocCString('hello world'); -var type = ref.coerceType('int **'); -var val = ref.deref(intBuf); -ref.isNull(new Buffer(1)); -var str = ref.readCString(new Buffer('hello\0world\0'), 0); -var buf1 = ref.alloc('int64'); -ref.writeInt64BE(buf1, 0, '9223372036854775807'); -var val = ref.readInt64BE(buf1, 0) -var voidPtrType = ref.refType(ref.types.void); -var buf2 = ref.alloc('int64'); -ref.writeInt64LE(buf2, 0, '9223372036854775807'); +{ + var sqlite3 = ref.types.void; + var sqlite3Ptr = ref.refType(sqlite3); + var sqlite3PtrPtr = ref.refType(sqlite3Ptr); + var stringPtr = ref.refType(ref.types.CString); + + var libsqlite3 = ffi.Library('libsqlite3', { + 'sqlite3_open': [ 'int', [ 'string', sqlite3PtrPtr ] ], + 'sqlite3_close': [ 'int', [ sqlite3PtrPtr ] ], + 'sqlite3_exec': [ 'int', [ sqlite3PtrPtr, 'string', 'pointer', 'pointer', stringPtr ] ], + 'sqlite3_changes': [ 'int', [ sqlite3PtrPtr ]] + }); + + var dbPtrPtr = ref.alloc(sqlite3PtrPtr); + libsqlite3.sqlite3_open("test.sqlite3", dbPtrPtr); + var dbHandle = dbPtrPtr.deref(); +} +{ + var func = ffi.ForeignFunction(new Buffer(10), 'int', [ 'int' ]); + func(-5); + func.async(-5, function(err, res) {}); +} +{ + var printfPointer = ffi.DynamicLibrary().get('printf'); + var printfGen = ffi.VariadicForeignFunction(printfPointer, 'void', [ 'string' ]); + printfGen()('Hello World!\n'); + printfGen('int')('This is an int: %d\n', 10); + printfGen('string')('This is a string: %s\n', 'hello'); +} +{ + ref.address(new Buffer(1)); + var intBuf = ref.alloc(ref.types.int); + var intWith4 = ref.alloc(ref.types.int, 4); + var buf0 = ref.allocCString('hello world'); + var type = ref.coerceType('int **'); + var val = ref.deref(intBuf); +} +{ + ref.isNull(new Buffer(1)); +} +{ + var str = ref.readCString(new Buffer('hello\0world\0'), 0); + var buf = ref.alloc('int64'); + ref.writeInt64BE(buf, 0, '9223372036854775807'); + var val = ref.readInt64BE(buf, 0) +} +{ + var voidPtrType = ref.refType(ref.types.void); + var buf = ref.alloc('int64'); + ref.writeInt64LE(buf, 0, '9223372036854775807'); +} diff --git a/node-ffi/node-ffi.d.ts b/node-ffi/node-ffi.d.ts index 2b6485fb2..7c94cc761 100644 --- a/node-ffi/node-ffi.d.ts +++ b/node-ffi/node-ffi.d.ts @@ -5,17 +5,19 @@ /// -declare module "ffi" {} +declare module "ffi" { + // import StructType = require('ref-struct'); + + export interface StructType {} -declare module "ref" { export interface Type { /** The size in bytes required to hold this datatype. */ size: number; /** The current level of indirection of the buffer. */ indirection: number; - /** To invoke when ref.get() is invoked on a buffer of this type. */ + /** To invoke when `ref.get` is invoked on a buffer of this type. */ get(buffer: NodeBuffer, offset: number): any; - /** To invoke when ref.set() is invoked on a buffer of this type. */ + /** To invoke when `ref.set` is invoked on a buffer of this type. */ set(buffer: NodeBuffer, offset: number, value): void; /** The name to use during debugging for this datatype. */ name?: string; @@ -23,14 +25,210 @@ declare module "ref" { alignment?: number; } + /** Provides a friendly API on-top of `DynamicLibrary` and `ForeignFunction`. */ + export var 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, funcs?: {[key: string]: any[]}, 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, funcs?: {[key: string]: any[]}, lib?: Object): any; + }; + + /** Get value of errno. */ + export function errno(): number; + + export interface Function extends Type { + /** The type of return value. */ + retType: Type; + /** The type of arguments. */ + argTypes: Type[]; + abi: number; + + /** Is set for node-ffi functions. */ + ffi_type: NodeBuffer; + /** The size in bytes required to hold this datatype. */ + size: number; + /** The current level of indirection of the buffer. */ + indirection: number; + /** To invoke when `ref.get` is invoked on a buffer of this type. */ + get(buffer: NodeBuffer, offset: number): any; + /** To invoke when `ref.set` is invoked on a buffer of this type. */ + set(buffer: NodeBuffer, offset: number, value): void; + /** The alignment of this datatype when placed inside a struct. */ + alignment: number; + + /** Get a `Callback` pointer of this function type. */ + toPointer(fn: (...args: any[]) => any): NodeBuffer; + /** Get a `ForeignFunction` of this function type. */ + toFunction(buf: NodeBuffer): ForeignFunction; + } + + /** Creates and returns a type for a C function pointer. */ + export var Function: { + new (retType: Type, argTypes: any[], abi?: number): Function; + new (retType: string, argTypes: any[], abi?: number): Function; + (retType: Type, argTypes: any[], abi?: number): Function; + (retType: string, argTypes: any[], abi?: number): Function; + }; + + export interface ForeignFunction { + (...args: any[]): any; + async(...args: any[]): 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 var ForeignFunction: { + new (ptr: NodeBuffer, retType: Type, argTypes: any[], abi?: number): ForeignFunction; + new (ptr: NodeBuffer, retType: string, argTypes: any[], abi?: number): ForeignFunction; + (ptr: NodeBuffer, retType: Type, argTypes: any[], abi?: number): ForeignFunction; + (ptr: NodeBuffer, retType: string, argTypes: any[], 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. + */ + (...args: any[]): ForeignFunction; + + /** + * 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: any; + } + + /** + * 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 var VariadicForeignFunction: { + new (ptr: NodeBuffer, ret: Type, fixedArgs: any[], abi?: number): VariadicForeignFunction; + new (ptr: NodeBuffer, ret: string, fixedArgs: any[], abi?: number): VariadicForeignFunction; + (ptr: NodeBuffer, ret: Type, fixedArgs: any[], abi?: number): VariadicForeignFunction; + (ptr: NodeBuffer, ret: string, fixedArgs: any[], abi?: number): VariadicForeignFunction; + }; + + export interface DynamicLibrary { + /** Close library, returns the result of the `dlclose` system function. */ + close(): number; + /** Get a symbol from this library. */ + get(symbol: string): NodeBuffer; + /** Get the result of the `dlerror` system function. */ + error(): 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 `get(symbol)` 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 `ForeignFunction`. + */ + export var DynamicLibrary: { + FLAGS: { + RTLD_LAZY: number; + RTLD_NOW: number; + RTLD_LOCAL: number; + RTLD_GLOBAL: number; + RTLD_NOLOAD: number; + RTLD_NODELETE: number; + RTLD_NEXT: NodeBuffer; + RTLD_DEFAUL: NodeBuffer; + } + + new (path?: string, mode?: number): DynamicLibrary; + (path?: string, mode?: number): DynamicLibrary; + }; + + /** + * 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 var Callback: { + new (retType, argTypes: any[], abi: number, fn: Function): NodeBuffer; + new (retType, argTypes: any[], fn: Function): NodeBuffer; + (retType, argTypes: any[], abi: number, fn: Function): NodeBuffer; + (retType, argTypes: any[], fn: Function): NodeBuffer; + } + + export var ffiType: { + /** Get a `ffi_type *` Buffer appropriate for the given type. */ + (type: Type): NodeBuffer + /** Get a `ffi_type *` Buffer appropriate for the given type. */ + (type: string): NodeBuffer + FFI_TYPE: StructType; + } + + export var CIF: Function; + export var CIF_var: Function; + export var HAS_OBJC: boolean; + export var FFI_TYPES: {[key: string]: NodeBuffer}; + export var FFI_OK: number; + export var FFI_BAD_TYPEDEF: number; + export var FFI_BAD_ABI: number; + export var FFI_DEFAULT_ABI: number; + export var FFI_FIRST_ABI: number; + export var FFI_LAST_ABI: number; + export var FFI_SYSV: number; + export var FFI_UNIX64: number; + export var RTLD_LAZY: number; + export var RTLD_NOW: number; + export var RTLD_LOCAL: number; + export var RTLD_GLOBAL: number; + export var RTLD_NOLOAD: number; + export var RTLD_NODELETE: number; + export var RTLD_NEXT: NodeBuffer; + export var RTLD_DEFAULT: NodeBuffer; + export var LIB_EXT: string; + export var FFI_TYPE: StructType; + + /** Default types. */ + export var types: { + void: Type; int64: Type; ushort: Type; + int: Type; uint64: Type; float: Type; + uint: Type; long: Type; double: Type; + int8: Type; ulong: Type; Object: Type; + uint8: Type; longlong: Type; CString: Type; + int16: Type; ulonglong: Type; bool: Type; + uint16: Type; char: Type; byte: Type; + int32: Type; uchar: Type; size_t: Type; + uint32: Type; short: Type; + }; +} + +declare module "ref" { + import ffi = require('ffi'); + /** A Buffer that references the C NULL pointer. */ export var NULL: NodeBuffer; - /** A pointer-sized Buffer instance pointing to NULL. */ + /** A pointer-sized buffer pointing to NULL. */ export var NULL_POINTER: NodeBuffer; /** Get the memory address of buffer. */ export function address(buffer: NodeBuffer): number; /** Allocate the memory with the given value written to it. */ - export function alloc(type: Type, value?): NodeBuffer; + export function alloc(type: ffi.Type, value?): NodeBuffer; /** Allocate the memory with the given value written to it. */ export function alloc(type: string, value?): NodeBuffer; @@ -42,9 +240,9 @@ declare module "ref" { export function allocCString(string: string, encoding?: string): NodeBuffer; /** Coerce a type.*/ - export function coerceType(type: Type): Type; + export function coerceType(type: ffi.Type): ffi.Type; /** Coerce a type. String are looked up from the ref.types object. */ - export function coerceType(type: string): Type; + export function coerceType(type: string): ffi.Type; /** * Get value after dereferencing buffer. @@ -55,31 +253,31 @@ declare module "ref" { export function deref(buffer: NodeBuffer): any; /** Create clone of the type, with decremented indirection level by 1. */ - export function derefType(type: Type): Type; + export function derefType(type: ffi.Type): ffi.Type; /** Create clone of the type, with decremented indirection level by 1. */ - export function derefType(type: string): Type; + export function derefType(type: string): ffi.Type; /** Represents the native endianness of the processor ("LE" or "BE"). */ export var endianness: string; /** Check the indirection level and return a dereferenced when necessary. */ - export function get(buffer: NodeBuffer, offset?: number, type?: Type): any; + export function get(buffer: NodeBuffer, offset?: number, type?: ffi.Type): any; /** Check the indirection level and return a dereferenced when necessary. */ export function get(buffer: NodeBuffer, offset?: number, type?: string): any; /** Get type of the buffer. Create a default type when none exists. */ - export function getType(buffer: NodeBuffer): Type; + export function getType(buffer: NodeBuffer): ffi.Type; /** Check the NULL. */ export function isNull(buffer: NodeBuffer): boolean; /** Read C string until the first NULL. */ export function readCString(buffer: NodeBuffer, offset?: number): string; /** - * Read a big-endian signed 64-bit int + * Read a big-endian signed 64-bit int. * If there is losing precision, then return a string, otherwise a number. * @return {number|string} */ export function readInt64BE(buffer: NodeBuffer, offset?: number): any; /** - * Read a little-endian signed 64-bit int + * Read a little-endian signed 64-bit int. * If there is losing precision, then return a string, otherwise a number. * @return {number|string} */ @@ -91,14 +289,14 @@ declare module "ref" { export function readPointer(buffer: NodeBuffer, offset?: number, length?: number): NodeBuffer; /** - * Read a big-endian unsigned 64-bit int + * Read a big-endian unsigned 64-bit int. * If there is losing precision, then return a string, otherwise a number. * @return {number|string} */ export function readUInt64BE(buffer: NodeBuffer, offset?: number): any; /** - * Read a little-endian unsigned 64-bit int + * Read a little-endian unsigned 64-bit int. * If there is losing precision, then return a string, otherwise a number. * @return {number|string} */ @@ -107,9 +305,9 @@ declare module "ref" { /** Create pointer to buffer. */ export function ref(buffer: NodeBuffer): NodeBuffer; /** Create clone of the type, with incremented indirection level by 1. */ - export function refType(type: Type): Type; + export function refType(type: ffi.Type): ffi.Type; /** Create clone of the type, with incremented indirection level by 1. */ - export function refType(type: string): Type; + export function refType(type: string): ffi.Type; /** * Create buffer with the specified size, with the same address as source. @@ -119,14 +317,14 @@ declare module "ref" { export function reinterpret(buffer: NodeBuffer, size: number, offset?: number): NodeBuffer; /** - * Scan past the boundary of the Buffer's length until it finds size number + * Scan past the boundary of the buffer's length until it finds size number * of aligned NULL bytes. */ export function reinterpretUntilZeros(buffer: NodeBuffer, size: number, offset?: number): NodeBuffer; /** Write pointer if the indirection is 1, otherwise write value. */ - export function set(buffer: NodeBuffer, offset: number, value, type?: Type): void; + export function set(buffer: NodeBuffer, offset: number, value, type?: ffi.Type): void; /** Write pointer if the indirection is 1, otherwise write value. */ export function set(buffer: NodeBuffer, offset: number, value, type?: string): void; /** Write the string as a NULL terminated. Default encoding is utf8. */ @@ -177,67 +375,68 @@ declare module "ref" { /** Same as ref.writeObject, except that this version does not attach object. */ export function _writeObject(buffer: NodeBuffer, offset: number, object: Object): void; + /** Default types. */ export var types: { - void: Type; int64: Type; ushort: Type; - int: Type; uint64: Type; float: Type; - uint: Type; long: Type; double: Type; - int8: Type; ulong: Type; Object: Type; - uint8: Type; longlong: Type; CString: Type; - int16: Type; ulonglong: Type; bool: Type; - uint16: Type; char: Type; byte: Type; - int32: Type; uchar: Type; size_t: Type; - uint32: Type; short: Type; + void: ffi.Type; int64: ffi.Type; ushort: ffi.Type; + int: ffi.Type; uint64: ffi.Type; float: ffi.Type; + uint: ffi.Type; long: ffi.Type; double: ffi.Type; + int8: ffi.Type; ulong: ffi.Type; Object: ffi.Type; + uint8: ffi.Type; longlong: ffi.Type; CString: ffi.Type; + int16: ffi.Type; ulonglong: ffi.Type; bool: ffi.Type; + uint16: ffi.Type; char: ffi.Type; byte: ffi.Type; + int32: ffi.Type; uchar: ffi.Type; size_t: ffi.Type; + uint32: ffi.Type; short: ffi.Type; }; } interface NodeBuffer { - /** Shorthand for ref.address. */ + /** Shorthand for `ref.address`. */ address(): number; - /** Shorthand for ref.deref. */ + /** Shorthand for `ref.deref`. */ deref(): any; - /** Shorthand for ref.isNull. */ + /** Shorthand for `ref.isNull`. */ isNull(): boolean; - /** Shorthand for ref.readCString. */ + /** Shorthand for `ref.readCString`. */ readCString(offset?: number): string; - /** Shorthand for ref.readInt64BE. */ + /** Shorthand for `ref.readInt64BE`. */ readInt64BE(offset?: number): string; - /** Shorthand for ref.readInt64LE. */ + /** Shorthand for `ref.readInt64LE`. */ readInt64LE(offset?: number): string; - /** Shorthand for ref.readObject. */ + /** Shorthand for `ref.readObject`. */ readObject(offset?: number): string; - /** Shorthand for ref.readPointer. */ + /** Shorthand for `ref.readPointer`. */ readPointer(offset?: number): string; - /** Shorthand for ref.readUInt64BE. */ + /** Shorthand for `ref.readUInt64BE`. */ readUInt64BE(offset?: number): string; - /** Shorthand for ref.readUInt64LE. */ + /** Shorthand for `ref.readUInt64LE`. */ readUInt64LE(offset?: number): string; - /** Shorthand for ref.ref. */ + /** Shorthand for `ref.ref`. */ ref(): NodeBuffer; - /** Shorthand for ref.reinterpret. */ + /** Shorthand for `ref.reinterpret`. */ reinterpret(size: number, offset?: number): NodeBuffer; - /** Shorthand for ref.reinterpretUntilZeros. */ + /** Shorthand for `ref.reinterpretUntilZeros`. */ reinterpretUntilZeros(size: number, offset?: number): NodeBuffer; - /** Shorthand for ref.writeCString. */ + /** Shorthand for `ref.writeCString`. */ writeCString(offset: number, string: string, encoding?: string): void; - /** Shorthand for ref.writeInt64BE. */ + /** Shorthand for `ref.writeInt64BE`. */ writeInt64BE(offset: number, input: number): any; - /** Shorthand for ref.writeInt64BE. */ + /** Shorthand for `ref.writeInt64BE`. */ writeInt64BE(offset: number, input: string): any; - /** Shorthand for ref.writeInt64LE. */ + /** Shorthand for `ref.writeInt64LE`. */ writeInt64LE(offset: number, input: number): any; - /** Shorthand for ref.writeInt64LE. */ + /** Shorthand for `ref.writeInt64LE`. */ writeInt64LE(offset: number, input: string): any; - /** Shorthand for ref.writeObject. */ + /** Shorthand for `ref.writeObject`. */ writeObject(offset: number, object: Object): void; - /** Shorthand for ref.writePointer. */ + /** Shorthand for `ref.writePointer`. */ writePointer(offset: number, pointer: NodeBuffer): void; - /** Shorthand for ref.writeUInt64BE. */ + /** Shorthand for `ref.writeUInt64BE`. */ writeUInt64BE(offset: number, input: number): any; - /** Shorthand for ref.writeUInt64BE. */ + /** Shorthand for `ref.writeUInt64BE`. */ writeUInt64BE(offset: number, input: string): any; - /** Shorthand for ref.writeUInt64LE. */ + /** Shorthand for `ref.writeUInt64LE`. */ writeUInt64LE(offset: number, input: number): any; - /** Shorthand for ref.writeUInt64LE. */ + /** Shorthand for `ref.writeUInt64LE`. */ writeUInt64LE(offset: number, input: string): any; /**