@jellybrick/ffi-cjs

0.7.0 • Public • Published

About

Foreign Function Interface helper. Provides a friendly abstraction/API for:

Syntax is inspired by Deno FFI. The goal was to be able to easily switch from ffi-napi to koffi or vice versa.

📦 Scoped @xan105 packages are for my own personal use but feel free to use them.

  • Forked form @xan105/ffi (MIT)

Example

Loading a library with Deno like syntax

import { dlopen } from "@jellybrick/ffi-cjs/[ napi | koffi ]";

const lib = dlopen("libm", {
  ceil: { 
    result: "double", 
    parameters: [ "double" ] 
  }
});
lib.ceil(1.5); // 2

Async

import { dlopen } from "@jellybrick/ffi-cjs/[ napi | koffi ]";

const lib = dlopen("libm", {
  ceil: { 
    result: "double", 
    parameters: [ "double" ],
    nonblocking: true 
  }
});
await lib.ceil(1.5); // 2

Calling directly from a library

import { load, types } from "@jellybrick/ffi-cjs/[ napi | koffi ]";

const call = load("user32.dll", { abi: "stdcall" });
const MessageBoxA = call("MessageBoxA", "int", [
  "void *", 
  types.win32.LPCSTR, 
  types.win32.LPCSTR, 
  "uint"
]);

const MB_ICONINFORMATION = 0x40;
MessageBoxA(null, "Hello World!", "Message", MB_ICONINFORMATION);

Callback with Deno like syntax

import { dlopen, Callback} from "@jellybrick/ffi-cjs/koffi";

const library = dlopen(
  "./callback.so",
  {
    set_status_callback: {
      parameters: ["function"],
      result: "void"
    },
    start_long_operation: {
      parameters: [],
      result: "void"
    }
  }
);

const callback = new Callback(
  {
    parameters: ["u8"],
    result: "void",
  },
  (success) => {}
);

library.set_status_callback(callback.pointer);
library.start_long_operation();

Install

npm install @jellybrick/ffi-cjs

Please note that ffi-napi and koffi are optional peer dependencies.
Install the one you wish to use yourself (or both 🙃).

API

⚠️ This module is only available as an CommonJS module (CJS).

💡 This lib doesn't have a default entry point. Choose the export corresponding to your liking.

TypeScript

import ... from "@jellybrick/ffi-cjs/napi";
//OR
import ... from "@jellybrick/ffi-cjs/koffi";

JavaScript

const ... = require("@jellybrick/ffi-cjs/napi");
//OR
const ... = require("@jellybrick/ffi-cjs/koffi");

Named export

load(path: string, option?: object): function

Load the given library path and return an handle function to call library's symbol(s).

Option

  • ignoreLoadingFail?: boolean (false)

Silent fail if the given library couldn't be loaded.
💡 Called symbol will be undefined in that case.

  • ignoreMissingSymbol?: boolean (false)

Silent fail if the given library doesn't have the called symbol.
💡 Called symbol will be undefined in that case.

  • abi?: string ("func" for koffi and "default_abi" for ffi-napi)

ABI convention to use. Use this when you need to ex: winapi x86 requires "stdcall".

Return

function(symbol: string | number, result: unknown, parameters: unknown[]): any;

💡 Koffi can call by ordinal (symbol:number)

See the corresponding FFI library for more information on what to pass for result and parameters as they have string type parser, structure/array/pointer interface, ... and other features.

Throws on error

Example:

import { load } from "@jellybrick/ffi-cjs/[ napi | koffi ]";
const call = load("libm");

const ceil = call("ceil", "double", ["double"])
ceil(1.5); //2

dlopen(path: string, symbols: object, option?: object): object

Open library and define exported symbols. This is a friendly wrapper to load() inspired by Deno FFI dlopen syntax.

If you ever use ffi-napi ffi.Library() this will be familiar.

Param

  • path: string

    Library path to load

  • symbols: object

    Symbol(s) definition:

  {
    name: {
      result?: unknown,
      parameters?: unknown[],
      nonblocking?: boolean,
      symbol?: string | number
    },
    ...
  }

By default the property name is used for symbol when omitted. Use symbol if you are using a different name than the symbol name or if you want to call by ordinal (Koffi).

When nonblocking is true (default false) this will return the promisified async() method of the corresponding symbol (see corresponding ffi library asynchronous calling). The rest is the same as for load().

  • option?: object

    Pass option(s) to load(). See above.

Return

An object with the given symbol(s) as properties.

Throws on error

Example

import { dlopen, types } from "@jellybrick/ffi-cjs/[ napi | koffi ]";
const { BOOL } = types.win32;

const lib = dlopen("xinput1_4", {
  "XInputEnable": {
    parameters: [BOOL],
    nonblocking: true
  }
}, { abi: "stdcall" });

await lib.XInputEnable(1);

const types: object

The FFI Library's primitive types as well as corresponding alias are exposed for convenience. Such as Deno types (rust) and Windows specific types (DWORD,...).

💡 Windows specific types are grouped together under win32.

import { types } from "@jellybrick/ffi-cjs/[ napi | koffi ]";
const { DWORD, LPCSTR } = types.win32;

💡 When using koffi alias are also set with koffi.alias() so you can use them as string.

import { load } from "@jellybrick/ffi-cjs/koffi";
const call = load("user32.dll", { abi: "stdcall" });
const MessageBoxA = call("MessageBoxA", "int", ["void *", "LPCSTR", "LPCSTR", "uint"]);

⚠️ Types are not exposed under their own namespace because some words are illegal or already in use in JavaScript. You can still use destructuring if needed as long as the name is "allowed".

No

import { i32 } from "@jellybrick/ffi-cjs/koffi/types"

✔️ Yes

import { types } from "@jellybrick/ffi-cjs/koffi"
const { i32 } = types;

🚫 Forbidden

import { types } from "@jellybrick/ffi-cjs/napi"
const { function } = types;

class Callback

Create a callback to be called at a later time (registered callback).

This is a class wrapper to the FFI library's callback function(s) inspired by Deno FFI UnsafeCallback class syntax.

Constructor

(definition: { result: unknown, parameters: unknown[] }, callback?: Function | null)

Properties
  • pointer: unknown

The pointer to the callback.

  • address: number | BigInt | null

The memory address of the pointer.

  • type: unknown

The type of the callback.

Methods
  • close(): void

Dispose of the callback. Remove function pointer associated with this instance.

  • register(callback?: Function): void

Register the callback. If a callback was already registered with this instance it will be disposed of.

Example
import { dlopen, types, Callback } from "@jellybrick/ffi-cjs/[ napi | koffi ]";

const library = dlopen("./callback.so", {
    setCallback: {
      parameters: [types.function],
      result: "void",
    },
    doSomething(): {
      parameters: [],
      result: "void",
    },
});

const callback = new Callback(
  { parameters: [], result: "void" },
  () => {},
);

library.setCallback(callback.pointer);
library.doSomething();

// After callback is no longer needed
callback.close();

You can also register the callback at a later time:

import { dlopen, Callback } from "@jellybrick/ffi-cjs/[ napi | koffi ]";

const callback = new Callback(
  { parameters: [], result: "void" }
);

const library = dlopen("./callback.so", {
    setCallback: {
      parameters: [callback.type],
      result: "void",
    },
    doSomething(): {
      parameters: [],
      result: "void",
    },
});

callback.register(()=>{});

library.setCallback(callback.pointer);
library.doSomething();

// After callback is no longer needed
callback.close();

pointer(value: unknown, direction?: string): any

Just a shorthand to ref.refType(x) (ffi-napi) and koffi.out/inout(koffi.pointer(x)) (koffi) to define a pointer.

alloc(type: unknown): { pointer: Buffer, get: ()=> unknown }

Allocate a buffer and get the corresponding data when passing a pointer to allow the called function to manipulate memory.

import { dlopen, alloc } from "@jellybrick/ffi-cjs/[ napi | koffi ]";
const dylib = dlopen(...); //lib loading

const number = alloc("int"); //allocate Buffer for the output data
dylib.manipulate_number(number.pointer);
const result = number.get();

Readme

Keywords

Package Sidebar

Install

npm i @jellybrick/ffi-cjs

Weekly Downloads

3

Version

0.7.0

License

MIT

Unpacked Size

33 kB

Total Files

19

Last publish

Collaborators

  • jellybrick