@lakutata/ffi 0.0.0-stage → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +163 -2
- package/README.zh-CN.md +140 -0
- package/dist/cjs/components/NativeLibrary.d.ts +229 -0
- package/dist/cjs/components/NativeLibrary.js +346 -0
- package/dist/cjs/decorators/Library.d.ts +61 -0
- package/dist/cjs/decorators/Library.js +44 -0
- package/dist/cjs/decorators/Native.d.ts +69 -0
- package/dist/cjs/decorators/Native.js +90 -0
- package/dist/cjs/exceptions/NativeDeclarationException.d.ts +12 -0
- package/dist/cjs/exceptions/NativeDeclarationException.js +19 -0
- package/dist/cjs/exceptions/NativeLibraryClosedException.d.ts +12 -0
- package/dist/cjs/exceptions/NativeLibraryClosedException.js +19 -0
- package/dist/cjs/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
- package/dist/cjs/exceptions/NativeLibraryNotFoundException.js +18 -0
- package/dist/cjs/exceptions/NativeMemoryException.d.ts +11 -0
- package/dist/cjs/exceptions/NativeMemoryException.js +18 -0
- package/dist/cjs/exports/FFI.d.ts +12 -0
- package/dist/cjs/exports/FFI.js +40 -0
- package/dist/cjs/exports/Generator.d.ts +2 -0
- package/dist/cjs/exports/Generator.js +10 -0
- package/dist/cjs/lib/Memory.d.ts +188 -0
- package/dist/cjs/lib/Memory.js +227 -0
- package/dist/cjs/lib/Runtime.d.ts +98 -0
- package/dist/cjs/lib/Runtime.js +209 -0
- package/dist/cjs/lib/backend/Functions.d.ts +65 -0
- package/dist/cjs/lib/backend/Functions.js +214 -0
- package/dist/cjs/lib/backend/KoffiTypes.d.ts +53 -0
- package/dist/cjs/lib/backend/KoffiTypes.js +426 -0
- package/dist/cjs/lib/declarations/Builtins.d.ts +33 -0
- package/dist/cjs/lib/declarations/Builtins.js +179 -0
- package/dist/cjs/lib/declarations/Layout.d.ts +41 -0
- package/dist/cjs/lib/declarations/Layout.js +180 -0
- package/dist/cjs/lib/declarations/Lexer.d.ts +40 -0
- package/dist/cjs/lib/declarations/Lexer.js +325 -0
- package/dist/cjs/lib/declarations/Parser.d.ts +90 -0
- package/dist/cjs/lib/declarations/Parser.js +1425 -0
- package/dist/cjs/lib/declarations/Platform.d.ts +50 -0
- package/dist/cjs/lib/declarations/Platform.js +27 -0
- package/dist/cjs/lib/declarations/Types.d.ts +173 -0
- package/dist/cjs/lib/declarations/Types.js +179 -0
- package/dist/cjs/lib/generator/CText.d.ts +14 -0
- package/dist/cjs/lib/generator/CText.js +133 -0
- package/dist/cjs/lib/generator/Generator.d.ts +136 -0
- package/dist/cjs/lib/generator/Generator.js +395 -0
- package/dist/cjs/lib/generator/Preprocessor.d.ts +60 -0
- package/dist/cjs/lib/generator/Preprocessor.js +94 -0
- package/dist/cjs/lib/generator/TypeScriptTypes.d.ts +35 -0
- package/dist/cjs/lib/generator/TypeScriptTypes.js +153 -0
- package/dist/cjs/lib/library/NativeCallback.d.ts +44 -0
- package/dist/cjs/lib/library/NativeCallback.js +66 -0
- package/dist/cjs/lib/library/NativeLibraryHandle.d.ts +268 -0
- package/dist/cjs/lib/library/NativeLibraryHandle.js +540 -0
- package/dist/cjs/lib/library/Resolve.d.ts +49 -0
- package/dist/cjs/lib/library/Resolve.js +130 -0
- package/dist/cjs/lib/thread/NativeThreads.d.ts +80 -0
- package/dist/cjs/lib/thread/NativeThreads.js +384 -0
- package/dist/cjs/lib/thread/NativeWorker.d.ts +6 -0
- package/dist/cjs/lib/thread/NativeWorker.js +96 -0
- package/dist/cjs/lib/thread/NativeWorkerCode.d.ts +5 -0
- package/dist/cjs/lib/thread/NativeWorkerCode.js +4 -0
- package/dist/cjs/lib/thread/PackageRequire.d.ts +6 -0
- package/dist/cjs/lib/thread/PackageRequire.js +4 -0
- package/dist/cjs/lib/thread/ThreadProtocol.d.ts +77 -0
- package/dist/cjs/lib/thread/ThreadProtocol.js +2 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/components/NativeLibrary.js +338 -0
- package/dist/esm/decorators/Library.js +40 -0
- package/dist/esm/decorators/Native.js +85 -0
- package/dist/esm/exceptions/NativeDeclarationException.js +15 -0
- package/dist/esm/exceptions/NativeLibraryClosedException.js +15 -0
- package/dist/esm/exceptions/NativeLibraryNotFoundException.js +14 -0
- package/dist/esm/exceptions/NativeMemoryException.js +14 -0
- package/dist/esm/exports/FFI.js +10 -0
- package/dist/esm/exports/Generator.js +2 -0
- package/dist/esm/lib/Memory.js +210 -0
- package/dist/esm/lib/Runtime.js +192 -0
- package/dist/esm/lib/backend/Functions.js +206 -0
- package/dist/esm/lib/backend/KoffiTypes.js +418 -0
- package/dist/esm/lib/declarations/Builtins.js +174 -0
- package/dist/esm/lib/declarations/Layout.js +175 -0
- package/dist/esm/lib/declarations/Lexer.js +321 -0
- package/dist/esm/lib/declarations/Parser.js +1421 -0
- package/dist/esm/lib/declarations/Platform.js +23 -0
- package/dist/esm/lib/declarations/Types.js +170 -0
- package/dist/esm/lib/generator/CText.js +129 -0
- package/dist/esm/lib/generator/Generator.js +388 -0
- package/dist/esm/lib/generator/Preprocessor.js +87 -0
- package/dist/esm/lib/generator/TypeScriptTypes.js +149 -0
- package/dist/esm/lib/library/NativeCallback.js +59 -0
- package/dist/esm/lib/library/NativeLibraryHandle.js +532 -0
- package/dist/esm/lib/library/Resolve.js +122 -0
- package/dist/esm/lib/thread/NativeThreads.js +377 -0
- package/dist/esm/lib/thread/NativeWorker.js +93 -0
- package/dist/esm/lib/thread/NativeWorkerCode.js +1 -0
- package/dist/esm/lib/thread/PackageRequire.js +2 -0
- package/dist/esm/lib/thread/ThreadProtocol.js +1 -0
- package/dist/types/components/NativeLibrary.d.ts +229 -0
- package/dist/types/decorators/Library.d.ts +61 -0
- package/dist/types/decorators/Native.d.ts +69 -0
- package/dist/types/exceptions/NativeDeclarationException.d.ts +12 -0
- package/dist/types/exceptions/NativeLibraryClosedException.d.ts +12 -0
- package/dist/types/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
- package/dist/types/exceptions/NativeMemoryException.d.ts +11 -0
- package/dist/types/exports/FFI.d.ts +12 -0
- package/dist/types/exports/Generator.d.ts +2 -0
- package/dist/types/lib/Memory.d.ts +188 -0
- package/dist/types/lib/Runtime.d.ts +98 -0
- package/dist/types/lib/backend/Functions.d.ts +65 -0
- package/dist/types/lib/backend/KoffiTypes.d.ts +53 -0
- package/dist/types/lib/declarations/Builtins.d.ts +33 -0
- package/dist/types/lib/declarations/Layout.d.ts +41 -0
- package/dist/types/lib/declarations/Lexer.d.ts +40 -0
- package/dist/types/lib/declarations/Parser.d.ts +90 -0
- package/dist/types/lib/declarations/Platform.d.ts +50 -0
- package/dist/types/lib/declarations/Types.d.ts +173 -0
- package/dist/types/lib/generator/CText.d.ts +14 -0
- package/dist/types/lib/generator/Generator.d.ts +136 -0
- package/dist/types/lib/generator/Preprocessor.d.ts +60 -0
- package/dist/types/lib/generator/TypeScriptTypes.d.ts +35 -0
- package/dist/types/lib/library/NativeCallback.d.ts +44 -0
- package/dist/types/lib/library/NativeLibraryHandle.d.ts +268 -0
- package/dist/types/lib/library/Resolve.d.ts +49 -0
- package/dist/types/lib/thread/NativeThreads.d.ts +80 -0
- package/dist/types/lib/thread/NativeWorker.d.ts +6 -0
- package/dist/types/lib/thread/NativeWorkerCode.d.ts +5 -0
- package/dist/types/lib/thread/PackageRequire.d.ts +6 -0
- package/dist/types/lib/thread/ThreadProtocol.d.ts +77 -0
- package/package.json +85 -4
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BUILTIN_TYPES = exports.TypeNames = exports.TYPES = void 0;
|
|
7
|
+
exports.checkedPointer = checkedPointer;
|
|
8
|
+
exports.copyBuffer = copyBuffer;
|
|
9
|
+
exports.nativeView = nativeView;
|
|
10
|
+
exports.readNative = readNative;
|
|
11
|
+
exports.writeNative = writeNative;
|
|
12
|
+
exports.allocateNative = allocateNative;
|
|
13
|
+
exports.freeNative = freeNative;
|
|
14
|
+
exports.nativeErrno = nativeErrno;
|
|
15
|
+
exports.nativeWindowsError = nativeWindowsError;
|
|
16
|
+
exports.typeSize = typeSize;
|
|
17
|
+
const koffi_1 = __importDefault(require("koffi"));
|
|
18
|
+
const NativeDeclarationException_js_1 = require("../exceptions/NativeDeclarationException.js");
|
|
19
|
+
const NativeMemoryException_js_1 = require("../exceptions/NativeMemoryException.js");
|
|
20
|
+
const KoffiTypes_js_1 = require("./backend/KoffiTypes.js");
|
|
21
|
+
const Parser_js_1 = require("./declarations/Parser.js");
|
|
22
|
+
const Platform_js_1 = require("./declarations/Platform.js");
|
|
23
|
+
const Resolve_js_1 = require("./library/Resolve.js");
|
|
24
|
+
/**
|
|
25
|
+
* The koffi types of the process: one per C type, whatever library declares it
|
|
26
|
+
*/
|
|
27
|
+
exports.TYPES = new KoffiTypes_js_1.KoffiTypes(Platform_js_1.CURRENT_PLATFORM);
|
|
28
|
+
/**
|
|
29
|
+
* The C types read from their names, with the declarations of a library or the built-in types only
|
|
30
|
+
*/
|
|
31
|
+
class TypeNames {
|
|
32
|
+
#parser;
|
|
33
|
+
#types = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* The type names of a library.
|
|
36
|
+
* @param parser Its declarations.
|
|
37
|
+
*/
|
|
38
|
+
constructor(parser) {
|
|
39
|
+
this.#parser = parser;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A type, from its name.
|
|
43
|
+
* @param text Its name: `int`, `struct timeval`, `const char *`.
|
|
44
|
+
*/
|
|
45
|
+
type(text) {
|
|
46
|
+
let type = this.#types.get(text);
|
|
47
|
+
if (!type) {
|
|
48
|
+
type = this.#parser.typeName(text);
|
|
49
|
+
this.#types.set(text, type);
|
|
50
|
+
}
|
|
51
|
+
return type;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The koffi type of a type name.
|
|
55
|
+
* @param text Its name.
|
|
56
|
+
*/
|
|
57
|
+
koffi(text) {
|
|
58
|
+
return exports.TYPES.type(this.type(text));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.TypeNames = TypeNames;
|
|
62
|
+
/**
|
|
63
|
+
* The type names of the built-in types (of no library)
|
|
64
|
+
*/
|
|
65
|
+
exports.BUILTIN_TYPES = new TypeNames(new Parser_js_1.DeclarationParser(Platform_js_1.CURRENT_PLATFORM));
|
|
66
|
+
let copier;
|
|
67
|
+
/**
|
|
68
|
+
* memcpy of the C library, copying native memory into buffers
|
|
69
|
+
*/
|
|
70
|
+
function memcpy() {
|
|
71
|
+
if (!copier) {
|
|
72
|
+
const library = process.platform === 'win32' ? koffi_1.default.load('msvcrt.dll') : (0, Resolve_js_1.loadLibrary)('c').library;
|
|
73
|
+
copier = library.func('memcpy', 'void *', ['void *', 'void *', 'size_t']);
|
|
74
|
+
}
|
|
75
|
+
return copier;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A pointer checked: not null, a bigint or a number
|
|
79
|
+
* @param pointer
|
|
80
|
+
* @param what
|
|
81
|
+
*/
|
|
82
|
+
function checkedPointer(pointer, what) {
|
|
83
|
+
if (pointer === null || pointer === undefined || pointer === 0n || pointer === 0)
|
|
84
|
+
throw new NativeMemoryException_js_1.NativeMemoryException(`${what}: the pointer is null`);
|
|
85
|
+
if (typeof pointer === 'number')
|
|
86
|
+
return BigInt(pointer);
|
|
87
|
+
if (typeof pointer !== 'bigint')
|
|
88
|
+
throw new NativeMemoryException_js_1.NativeMemoryException(`${what}: a pointer (a bigint) is expected, not ${typeof pointer}`);
|
|
89
|
+
return pointer;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A length checked: a non-negative integer
|
|
93
|
+
* @param length
|
|
94
|
+
* @param what
|
|
95
|
+
*/
|
|
96
|
+
function checkedLength(length, what) {
|
|
97
|
+
if (!Number.isInteger(length) || length < 0)
|
|
98
|
+
throw new NativeMemoryException_js_1.NativeMemoryException(`${what}: the length must be a non-negative integer (${length})`);
|
|
99
|
+
return length;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A copy of native memory, in a buffer of its own.
|
|
103
|
+
* @param pointer The address of the memory.
|
|
104
|
+
* @param length Its length in bytes.
|
|
105
|
+
*/
|
|
106
|
+
function copyBuffer(pointer, length) {
|
|
107
|
+
const buffer = Buffer.allocUnsafe(checkedLength(length, 'readBuffer'));
|
|
108
|
+
if (length)
|
|
109
|
+
memcpy()(buffer, checkedPointer(pointer, 'readBuffer'), length);
|
|
110
|
+
return buffer;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A view of native memory, without copy: refused in Electron, which forbids them (koffi would end the process).
|
|
114
|
+
* @param pointer The address of the memory.
|
|
115
|
+
* @param length Its length in bytes.
|
|
116
|
+
*/
|
|
117
|
+
function nativeView(pointer, length) {
|
|
118
|
+
if (process.versions.electron)
|
|
119
|
+
throw new NativeMemoryException_js_1.NativeMemoryException('Electron does not allow views of native memory: use readBuffer, which copies it');
|
|
120
|
+
return koffi_1.default.view(checkedPointer(pointer, 'viewBuffer'), checkedLength(length, 'viewBuffer'));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A value read from native memory, converted as the package gives values (64-bit integers as bigints).
|
|
124
|
+
* @param names The type names to read the type with.
|
|
125
|
+
* @param pointer The address.
|
|
126
|
+
* @param type The type name.
|
|
127
|
+
* @param count The number of values, for an array.
|
|
128
|
+
*/
|
|
129
|
+
function readNative(names, pointer, type, count) {
|
|
130
|
+
const cType = names.type(type);
|
|
131
|
+
const address = checkedPointer(pointer, 'read');
|
|
132
|
+
const converter = exports.TYPES.converter(cType);
|
|
133
|
+
if (count === undefined) {
|
|
134
|
+
const value = koffi_1.default.decode(address, exports.TYPES.type(cType));
|
|
135
|
+
return converter?.fromNative ? converter.fromNative(value) : value;
|
|
136
|
+
}
|
|
137
|
+
const values = Array.from(koffi_1.default.decode(address, exports.TYPES.type(cType), checkedLength(count, 'readArray')));
|
|
138
|
+
return converter?.fromNative ? values.map(converter.fromNative) : values;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A value written to native memory.
|
|
142
|
+
* @param names The type names to read the type with.
|
|
143
|
+
* @param pointer The address.
|
|
144
|
+
* @param type The type name.
|
|
145
|
+
* @param value The value.
|
|
146
|
+
*/
|
|
147
|
+
function writeNative(names, pointer, type, value) {
|
|
148
|
+
const cType = names.type(type);
|
|
149
|
+
const converter = exports.TYPES.converter(cType);
|
|
150
|
+
koffi_1.default.encode(checkedPointer(pointer, 'write'), exports.TYPES.type(cType), converter?.toNative ? converter.toNative(value) : value);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Native memory allocated for values of a type (C's malloc: freed by freeNative).
|
|
154
|
+
* @param names The type names to read the type with.
|
|
155
|
+
* @param type The type name.
|
|
156
|
+
* @param count The number of values.
|
|
157
|
+
*/
|
|
158
|
+
function allocateNative(names, type, count = 1) {
|
|
159
|
+
if (!Number.isInteger(count) || count < 1)
|
|
160
|
+
throw new NativeMemoryException_js_1.NativeMemoryException(`allocMemory: the count must be a positive integer (${count})`);
|
|
161
|
+
const pointer = koffi_1.default.alloc(names.koffi(type), count);
|
|
162
|
+
return typeof pointer === 'bigint' ? pointer : koffi_1.default.address(pointer);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Free native memory allocated by allocateNative (or by the malloc of the C library the package is built with).
|
|
166
|
+
* @param pointer The address.
|
|
167
|
+
*/
|
|
168
|
+
function freeNative(pointer) {
|
|
169
|
+
koffi_1.default.free(checkedPointer(pointer, 'freeMemory'));
|
|
170
|
+
}
|
|
171
|
+
//The _errno() of each C library of Windows
|
|
172
|
+
const windowsErrno = new Map();
|
|
173
|
+
let windowsLastError;
|
|
174
|
+
/**
|
|
175
|
+
* The errno of the last call made by the thread: on Windows, the one of the C library of Microsoft (ucrtbase.dll), each
|
|
176
|
+
* C library having its own; elsewhere the one koffi keeps after each call.
|
|
177
|
+
* @param crt On Windows, the C library the function called uses.
|
|
178
|
+
*/
|
|
179
|
+
function nativeErrno(crt = 'ucrtbase.dll') {
|
|
180
|
+
if (process.platform !== 'win32')
|
|
181
|
+
return koffi_1.default.errno();
|
|
182
|
+
const name = crt.toLowerCase();
|
|
183
|
+
let errno = windowsErrno.get(name);
|
|
184
|
+
if (!errno) {
|
|
185
|
+
errno = koffi_1.default.load(crt).func('_errno', 'int *', []);
|
|
186
|
+
windowsErrno.set(name, errno);
|
|
187
|
+
}
|
|
188
|
+
return koffi_1.default.decode(errno(), 'int');
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The last error of the Windows API of the thread (GetLastError), kept by koffi after each call; undefined elsewhere.
|
|
192
|
+
*/
|
|
193
|
+
function nativeWindowsError() {
|
|
194
|
+
if (process.platform !== 'win32')
|
|
195
|
+
return undefined;
|
|
196
|
+
windowsLastError ??= koffi_1.default.load('kernel32.dll').func('__stdcall', 'GetLastError', 'uint32_t', []);
|
|
197
|
+
return windowsLastError();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The size of a type on the platform, by its name.
|
|
201
|
+
* @param names The type names.
|
|
202
|
+
* @param type The type name.
|
|
203
|
+
*/
|
|
204
|
+
function typeSize(names, type) {
|
|
205
|
+
const cType = names.type(type);
|
|
206
|
+
if (cType.kind === 'function')
|
|
207
|
+
throw new NativeDeclarationException_js_1.NativeDeclarationException(`A function has no size: ${type}`);
|
|
208
|
+
return koffi_1.default.sizeof(exports.TYPES.type(cType));
|
|
209
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type LibraryHandle } from 'koffi';
|
|
2
|
+
import { type FunctionDeclaration, type FunctionType } from '../declarations/Types.js';
|
|
3
|
+
import { type KoffiType, type KoffiTypes } from './KoffiTypes.js';
|
|
4
|
+
/**
|
|
5
|
+
* How a function is bound
|
|
6
|
+
*/
|
|
7
|
+
export interface BindOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Whether it runs in a thread of the pool, returning a promise
|
|
10
|
+
*/
|
|
11
|
+
readonly async?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Frees the strings it gives (its result, its output strings) once read
|
|
14
|
+
*/
|
|
15
|
+
readonly free?: (pointer: unknown) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Its 64-bit integers (its result, its output integers) as numbers rather than bigints: a RangeError when one is not
|
|
18
|
+
* a safe integer
|
|
19
|
+
*/
|
|
20
|
+
readonly int64?: 'bigint' | 'number';
|
|
21
|
+
/**
|
|
22
|
+
* The asynchronous calls of the library: counted, refused once it is closed
|
|
23
|
+
*/
|
|
24
|
+
readonly calls?: CallTracker;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Counts the asynchronous calls of a library
|
|
28
|
+
*/
|
|
29
|
+
export interface CallTracker {
|
|
30
|
+
/**
|
|
31
|
+
* Before a call: throws once the library is closed
|
|
32
|
+
*/
|
|
33
|
+
start(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Once a call has returned
|
|
36
|
+
*/
|
|
37
|
+
end(): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The koffi types of the extra arguments of a variadic function, by the C type names given before them
|
|
41
|
+
*/
|
|
42
|
+
export type VariadicTypes = (text: string) => KoffiType;
|
|
43
|
+
/**
|
|
44
|
+
* A 64-bit integer as a number, when it is a safe one
|
|
45
|
+
* @param value
|
|
46
|
+
*/
|
|
47
|
+
export declare function toSafeNumber(value: unknown): unknown;
|
|
48
|
+
/**
|
|
49
|
+
* A JS function given as a callback, its arguments and its result converted when its prototype has 64-bit integers or
|
|
50
|
+
* structs with bitfields; the same function otherwise
|
|
51
|
+
* @param type
|
|
52
|
+
* @param types
|
|
53
|
+
* @param callback
|
|
54
|
+
*/
|
|
55
|
+
export declare function convertedCallback(type: FunctionType, types: KoffiTypes, callback: (...args: any[]) => any): (...args: any[]) => any;
|
|
56
|
+
/**
|
|
57
|
+
* A function of a library, bound by its declaration: the function of koffi itself when nothing has to be converted,
|
|
58
|
+
* else a function converting its arguments and its result; a function returning a promise when asynchronous
|
|
59
|
+
* @param library
|
|
60
|
+
* @param declaration
|
|
61
|
+
* @param types
|
|
62
|
+
* @param variadicTypes
|
|
63
|
+
* @param options
|
|
64
|
+
*/
|
|
65
|
+
export declare function bindFunction(library: LibraryHandle, declaration: FunctionDeclaration, types: KoffiTypes, variadicTypes: VariadicTypes, options?: BindOptions): (...args: any[]) => any;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.toSafeNumber = toSafeNumber;
|
|
7
|
+
exports.convertedCallback = convertedCallback;
|
|
8
|
+
exports.bindFunction = bindFunction;
|
|
9
|
+
const koffi_1 = __importDefault(require("koffi"));
|
|
10
|
+
const NativeDeclarationException_js_1 = require("../../exceptions/NativeDeclarationException.js");
|
|
11
|
+
const Types_js_1 = require("../declarations/Types.js");
|
|
12
|
+
const KoffiTypes_js_1 = require("./KoffiTypes.js");
|
|
13
|
+
/**
|
|
14
|
+
* A 64-bit integer as a number, when it is a safe one
|
|
15
|
+
* @param value
|
|
16
|
+
*/
|
|
17
|
+
function toSafeNumber(value) {
|
|
18
|
+
if (typeof value !== 'bigint')
|
|
19
|
+
return value;
|
|
20
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER))
|
|
21
|
+
throw new RangeError(`${value} is not a safe integer: declare the function without int64: 'number' to get a bigint`);
|
|
22
|
+
return Number(value);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether a type is a pointer to a string type (char *, char16_t *, wchar_t *)
|
|
26
|
+
* @param type
|
|
27
|
+
*/
|
|
28
|
+
function isString(type) {
|
|
29
|
+
return type.kind === 'pointer' && type.to.kind === 'primitive' && ['char', 'char16', 'wchar'].includes(type.to.name);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The koffi type of a string type, freed by a function once read
|
|
33
|
+
* @param type
|
|
34
|
+
* @param free
|
|
35
|
+
*/
|
|
36
|
+
function disposableString(type, free) {
|
|
37
|
+
const name = type.to.name;
|
|
38
|
+
return koffi_1.default.disposable(name === 'char' ? 'char *' : name === 'char16' ? 'char16_t *' : 'wchar_t *', free);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A JS function given as a callback, its arguments and its result converted when its prototype has 64-bit integers or
|
|
42
|
+
* structs with bitfields; the same function otherwise
|
|
43
|
+
* @param type
|
|
44
|
+
* @param types
|
|
45
|
+
* @param callback
|
|
46
|
+
*/
|
|
47
|
+
function convertedCallback(type, types, callback) {
|
|
48
|
+
const steps = type.parameters.map((parameter) => types.converter(parameter.type));
|
|
49
|
+
const result = types.converter(type.result);
|
|
50
|
+
if (!steps.some((step) => !!step?.fromNative) && !result?.toNative)
|
|
51
|
+
return callback;
|
|
52
|
+
return (...args) => {
|
|
53
|
+
for (let i = 0; i < steps.length; i++)
|
|
54
|
+
if (steps[i]?.fromNative)
|
|
55
|
+
args[i] = steps[i].fromNative(args[i]);
|
|
56
|
+
const value = callback(...args);
|
|
57
|
+
return result?.toNative ? result.toNative(value) : value;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The steps of the arguments of a function: the callbacks converted, the structs with bitfields written, the output
|
|
62
|
+
* integers and structs read back as the package gives them
|
|
63
|
+
* @param type
|
|
64
|
+
* @param types
|
|
65
|
+
* @param options
|
|
66
|
+
*/
|
|
67
|
+
function argumentSteps(type, types, options) {
|
|
68
|
+
const steps = [];
|
|
69
|
+
type.parameters.forEach((parameter, index) => {
|
|
70
|
+
const target = parameter.type.kind === 'pointer' ? parameter.type.to : undefined;
|
|
71
|
+
if (parameter.direction === 'in') {
|
|
72
|
+
if (target?.kind === 'function') {
|
|
73
|
+
const prototype = target;
|
|
74
|
+
const probe = () => undefined;
|
|
75
|
+
if (convertedCallback(prototype, types, probe) === probe)
|
|
76
|
+
return;
|
|
77
|
+
steps.push({ index: index, before: (value) => typeof value === 'function' ? convertedCallback(prototype, types, value) : value });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const converter = types.converter(target && (target.kind === 'struct' || target.kind === 'union') ? target : parameter.type);
|
|
81
|
+
if (converter?.toNative && (parameter.type.kind !== 'pointer' || target?.kind === 'struct' || target?.kind === 'union')) {
|
|
82
|
+
steps.push({ index: index, before: (value) => isPlainObject(value) ? converter.toNative(value) : value });
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!target)
|
|
87
|
+
return;
|
|
88
|
+
//An output integer: the array given gets a bigint (or a number, with int64: 'number')
|
|
89
|
+
if ((target.kind === 'primitive' && (0, Types_js_1.primitiveBig)(target.name)) || (target.kind === 'enum' && (0, Types_js_1.primitiveBig)(target.base))) {
|
|
90
|
+
const convert = options.int64 === 'number' ? toSafeNumber : KoffiTypes_js_1.toBigInt;
|
|
91
|
+
steps.push({ index: index, after: (given) => {
|
|
92
|
+
if (Array.isArray(given))
|
|
93
|
+
given[0] = convert(given[0]);
|
|
94
|
+
} });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
//An output struct: filled through an object koffi knows, then given back as the package gives it
|
|
98
|
+
const converter = types.converter(target);
|
|
99
|
+
if (converter && (target.kind === 'struct' || target.kind === 'union')) {
|
|
100
|
+
steps.push({
|
|
101
|
+
index: index,
|
|
102
|
+
before: (value) => isPlainObject(value) ? (parameter.direction === 'inout' && converter.toNative ? converter.toNative(value) : { ...value }) : value,
|
|
103
|
+
after: (given, passed) => {
|
|
104
|
+
if (isPlainObject(given) && passed !== given)
|
|
105
|
+
Object.assign(given, converter.fromNative ? converter.fromNative(passed) : passed);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return steps;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Whether a value is a plain object (a struct given as an object), not a pointer, a buffer or a koffi union
|
|
114
|
+
* @param value
|
|
115
|
+
*/
|
|
116
|
+
function isPlainObject(value) {
|
|
117
|
+
return !!value && typeof value === 'object' && !ArrayBuffer.isView(value) && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A function of a library, bound by its declaration: the function of koffi itself when nothing has to be converted,
|
|
121
|
+
* else a function converting its arguments and its result; a function returning a promise when asynchronous
|
|
122
|
+
* @param library
|
|
123
|
+
* @param declaration
|
|
124
|
+
* @param types
|
|
125
|
+
* @param variadicTypes
|
|
126
|
+
* @param options
|
|
127
|
+
*/
|
|
128
|
+
function bindFunction(library, declaration, types, variadicTypes, options = {}) {
|
|
129
|
+
const type = declaration.type;
|
|
130
|
+
if (options.async && type.variadic)
|
|
131
|
+
throw new NativeDeclarationException_js_1.NativeDeclarationException(`The variadic function ${declaration.name} cannot be called asynchronously`);
|
|
132
|
+
const result = options.free && isString(type.result) ? disposableString(type.result, options.free) : types.type(type.result);
|
|
133
|
+
const parameters = type.parameters.map((parameter) => {
|
|
134
|
+
const target = parameter.type.kind === 'pointer' ? parameter.type.to : undefined;
|
|
135
|
+
if (parameter.direction === 'in')
|
|
136
|
+
return types.type(parameter.type);
|
|
137
|
+
if (!target)
|
|
138
|
+
throw new NativeDeclarationException_js_1.NativeDeclarationException(`The parameter ${parameter.name ?? ''} of ${declaration.name} is ${parameter.direction === 'out' ? '_Out_' : '_Inout_'} but not a pointer (${(0, Types_js_1.typeText)(parameter.type)})`.replace(' ', ' '));
|
|
139
|
+
const pointed = options.free && isString(target) ? koffi_1.default.pointer(disposableString(target, options.free)) : types.type(parameter.type);
|
|
140
|
+
return parameter.direction === 'out' ? koffi_1.default.out(pointed) : koffi_1.default.inout(pointed);
|
|
141
|
+
});
|
|
142
|
+
let bound;
|
|
143
|
+
try {
|
|
144
|
+
bound = library.func(`__${type.convention}`, declaration.symbol, result, type.variadic ? [...parameters, '...'] : parameters);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
throw new NativeDeclarationException_js_1.NativeDeclarationException(`The function ${declaration.name} cannot be bound (${declaration.symbol}): ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
const steps = argumentSteps(type, types, options);
|
|
150
|
+
const resultConverter = options.int64 === 'number' && (type.result.kind === 'primitive' && (0, Types_js_1.primitiveBig)(type.result.name)) ? toSafeNumber : types.converter(type.result)?.fromNative;
|
|
151
|
+
const fixed = type.parameters.length;
|
|
152
|
+
const prepare = (args) => {
|
|
153
|
+
const passed = args.slice();
|
|
154
|
+
for (const step of steps)
|
|
155
|
+
if (step.before)
|
|
156
|
+
passed[step.index] = step.before(args[step.index]);
|
|
157
|
+
//The extra arguments of a variadic function: the koffi type of each C type name given
|
|
158
|
+
if (type.variadic)
|
|
159
|
+
for (let i = fixed; i < passed.length; i += 2)
|
|
160
|
+
if (typeof passed[i] === 'string')
|
|
161
|
+
passed[i] = variadicTypes(passed[i]);
|
|
162
|
+
return passed;
|
|
163
|
+
};
|
|
164
|
+
const finish = (args, passed, value) => {
|
|
165
|
+
for (const step of steps)
|
|
166
|
+
if (step.after)
|
|
167
|
+
step.after(args[step.index], passed[step.index]);
|
|
168
|
+
return resultConverter ? resultConverter(value) : value;
|
|
169
|
+
};
|
|
170
|
+
if (!options.async) {
|
|
171
|
+
if (!steps.length && !resultConverter && !type.variadic)
|
|
172
|
+
return bound;
|
|
173
|
+
return (...args) => {
|
|
174
|
+
const passed = prepare(args);
|
|
175
|
+
return finish(args, passed, bound(...passed));
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return (...args) => {
|
|
179
|
+
//Refused once the library is closed: a rejected promise, as every failure of the call
|
|
180
|
+
try {
|
|
181
|
+
options.calls?.start();
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
return Promise.reject(error);
|
|
185
|
+
}
|
|
186
|
+
let passed;
|
|
187
|
+
try {
|
|
188
|
+
passed = prepare(args);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
options.calls?.end();
|
|
192
|
+
return Promise.reject(error);
|
|
193
|
+
}
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
try {
|
|
196
|
+
bound.async(...passed, (error, value) => {
|
|
197
|
+
options.calls?.end();
|
|
198
|
+
if (error)
|
|
199
|
+
return reject(error);
|
|
200
|
+
try {
|
|
201
|
+
resolve(finish(args, passed, value));
|
|
202
|
+
}
|
|
203
|
+
catch (conversion) {
|
|
204
|
+
reject(conversion);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
options.calls?.end();
|
|
210
|
+
reject(error);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import koffi, { type TypeObject } from 'koffi';
|
|
2
|
+
import type { Platform } from '../declarations/Platform.js';
|
|
3
|
+
import { type CType, type FunctionType } from '../declarations/Types.js';
|
|
4
|
+
/**
|
|
5
|
+
* A type as koffi takes it: its name, its object, or its object with a direction (an output parameter)
|
|
6
|
+
*/
|
|
7
|
+
export type KoffiType = string | TypeObject | ReturnType<typeof koffi.out>;
|
|
8
|
+
/**
|
|
9
|
+
* How a value goes between the user and koffi, when koffi does not give or take it as the package does: the 64-bit
|
|
10
|
+
* integers always bigints, the bitfields of a struct read and written by their names
|
|
11
|
+
*/
|
|
12
|
+
export interface Converter {
|
|
13
|
+
/**
|
|
14
|
+
* A value of the user to the value koffi takes
|
|
15
|
+
*/
|
|
16
|
+
readonly toNative?: (value: any) => any;
|
|
17
|
+
/**
|
|
18
|
+
* A value koffi gives to the value of the user
|
|
19
|
+
*/
|
|
20
|
+
readonly fromNative?: (value: any) => any;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A value of a 64-bit integer type, as a bigint
|
|
24
|
+
* @param value
|
|
25
|
+
*/
|
|
26
|
+
export declare function toBigInt(value: unknown): unknown;
|
|
27
|
+
/**
|
|
28
|
+
* The koffi types of the C types of the declarations, made once each (koffi never frees a type): anonymous, so the
|
|
29
|
+
* types of two libraries never meet; the structs koffi cannot lay out (bitfields, packing) laid out by the package
|
|
30
|
+
*/
|
|
31
|
+
export declare class KoffiTypes {
|
|
32
|
+
#private;
|
|
33
|
+
/**
|
|
34
|
+
* The koffi types of a platform.
|
|
35
|
+
* @param platform The platform of the process.
|
|
36
|
+
*/
|
|
37
|
+
constructor(platform: Platform);
|
|
38
|
+
/**
|
|
39
|
+
* The koffi type of a C type, as a value: a member, a result, a parameter without direction.
|
|
40
|
+
* @param type The C type.
|
|
41
|
+
*/
|
|
42
|
+
type(type: CType): KoffiType;
|
|
43
|
+
/**
|
|
44
|
+
* The koffi prototype of a function type: the type of its callbacks.
|
|
45
|
+
* @param type The function type.
|
|
46
|
+
*/
|
|
47
|
+
prototype(type: FunctionType): TypeObject;
|
|
48
|
+
/**
|
|
49
|
+
* The converter of a C type, null when koffi gives and takes its values as the package does.
|
|
50
|
+
* @param type The C type.
|
|
51
|
+
*/
|
|
52
|
+
converter(type: CType): Converter | null;
|
|
53
|
+
}
|