@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.
Files changed (129) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +163 -2
  3. package/README.zh-CN.md +140 -0
  4. package/dist/cjs/components/NativeLibrary.d.ts +229 -0
  5. package/dist/cjs/components/NativeLibrary.js +346 -0
  6. package/dist/cjs/decorators/Library.d.ts +61 -0
  7. package/dist/cjs/decorators/Library.js +44 -0
  8. package/dist/cjs/decorators/Native.d.ts +69 -0
  9. package/dist/cjs/decorators/Native.js +90 -0
  10. package/dist/cjs/exceptions/NativeDeclarationException.d.ts +12 -0
  11. package/dist/cjs/exceptions/NativeDeclarationException.js +19 -0
  12. package/dist/cjs/exceptions/NativeLibraryClosedException.d.ts +12 -0
  13. package/dist/cjs/exceptions/NativeLibraryClosedException.js +19 -0
  14. package/dist/cjs/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
  15. package/dist/cjs/exceptions/NativeLibraryNotFoundException.js +18 -0
  16. package/dist/cjs/exceptions/NativeMemoryException.d.ts +11 -0
  17. package/dist/cjs/exceptions/NativeMemoryException.js +18 -0
  18. package/dist/cjs/exports/FFI.d.ts +12 -0
  19. package/dist/cjs/exports/FFI.js +40 -0
  20. package/dist/cjs/exports/Generator.d.ts +2 -0
  21. package/dist/cjs/exports/Generator.js +10 -0
  22. package/dist/cjs/lib/Memory.d.ts +188 -0
  23. package/dist/cjs/lib/Memory.js +227 -0
  24. package/dist/cjs/lib/Runtime.d.ts +98 -0
  25. package/dist/cjs/lib/Runtime.js +209 -0
  26. package/dist/cjs/lib/backend/Functions.d.ts +65 -0
  27. package/dist/cjs/lib/backend/Functions.js +214 -0
  28. package/dist/cjs/lib/backend/KoffiTypes.d.ts +53 -0
  29. package/dist/cjs/lib/backend/KoffiTypes.js +426 -0
  30. package/dist/cjs/lib/declarations/Builtins.d.ts +33 -0
  31. package/dist/cjs/lib/declarations/Builtins.js +179 -0
  32. package/dist/cjs/lib/declarations/Layout.d.ts +41 -0
  33. package/dist/cjs/lib/declarations/Layout.js +180 -0
  34. package/dist/cjs/lib/declarations/Lexer.d.ts +40 -0
  35. package/dist/cjs/lib/declarations/Lexer.js +325 -0
  36. package/dist/cjs/lib/declarations/Parser.d.ts +90 -0
  37. package/dist/cjs/lib/declarations/Parser.js +1425 -0
  38. package/dist/cjs/lib/declarations/Platform.d.ts +50 -0
  39. package/dist/cjs/lib/declarations/Platform.js +27 -0
  40. package/dist/cjs/lib/declarations/Types.d.ts +173 -0
  41. package/dist/cjs/lib/declarations/Types.js +179 -0
  42. package/dist/cjs/lib/generator/CText.d.ts +14 -0
  43. package/dist/cjs/lib/generator/CText.js +133 -0
  44. package/dist/cjs/lib/generator/Generator.d.ts +136 -0
  45. package/dist/cjs/lib/generator/Generator.js +395 -0
  46. package/dist/cjs/lib/generator/Preprocessor.d.ts +60 -0
  47. package/dist/cjs/lib/generator/Preprocessor.js +94 -0
  48. package/dist/cjs/lib/generator/TypeScriptTypes.d.ts +35 -0
  49. package/dist/cjs/lib/generator/TypeScriptTypes.js +153 -0
  50. package/dist/cjs/lib/library/NativeCallback.d.ts +44 -0
  51. package/dist/cjs/lib/library/NativeCallback.js +66 -0
  52. package/dist/cjs/lib/library/NativeLibraryHandle.d.ts +268 -0
  53. package/dist/cjs/lib/library/NativeLibraryHandle.js +540 -0
  54. package/dist/cjs/lib/library/Resolve.d.ts +49 -0
  55. package/dist/cjs/lib/library/Resolve.js +130 -0
  56. package/dist/cjs/lib/thread/NativeThreads.d.ts +80 -0
  57. package/dist/cjs/lib/thread/NativeThreads.js +384 -0
  58. package/dist/cjs/lib/thread/NativeWorker.d.ts +6 -0
  59. package/dist/cjs/lib/thread/NativeWorker.js +96 -0
  60. package/dist/cjs/lib/thread/NativeWorkerCode.d.ts +5 -0
  61. package/dist/cjs/lib/thread/NativeWorkerCode.js +4 -0
  62. package/dist/cjs/lib/thread/PackageRequire.d.ts +6 -0
  63. package/dist/cjs/lib/thread/PackageRequire.js +4 -0
  64. package/dist/cjs/lib/thread/ThreadProtocol.d.ts +77 -0
  65. package/dist/cjs/lib/thread/ThreadProtocol.js +2 -0
  66. package/dist/cjs/package.json +1 -0
  67. package/dist/esm/components/NativeLibrary.js +338 -0
  68. package/dist/esm/decorators/Library.js +40 -0
  69. package/dist/esm/decorators/Native.js +85 -0
  70. package/dist/esm/exceptions/NativeDeclarationException.js +15 -0
  71. package/dist/esm/exceptions/NativeLibraryClosedException.js +15 -0
  72. package/dist/esm/exceptions/NativeLibraryNotFoundException.js +14 -0
  73. package/dist/esm/exceptions/NativeMemoryException.js +14 -0
  74. package/dist/esm/exports/FFI.js +10 -0
  75. package/dist/esm/exports/Generator.js +2 -0
  76. package/dist/esm/lib/Memory.js +210 -0
  77. package/dist/esm/lib/Runtime.js +192 -0
  78. package/dist/esm/lib/backend/Functions.js +206 -0
  79. package/dist/esm/lib/backend/KoffiTypes.js +418 -0
  80. package/dist/esm/lib/declarations/Builtins.js +174 -0
  81. package/dist/esm/lib/declarations/Layout.js +175 -0
  82. package/dist/esm/lib/declarations/Lexer.js +321 -0
  83. package/dist/esm/lib/declarations/Parser.js +1421 -0
  84. package/dist/esm/lib/declarations/Platform.js +23 -0
  85. package/dist/esm/lib/declarations/Types.js +170 -0
  86. package/dist/esm/lib/generator/CText.js +129 -0
  87. package/dist/esm/lib/generator/Generator.js +388 -0
  88. package/dist/esm/lib/generator/Preprocessor.js +87 -0
  89. package/dist/esm/lib/generator/TypeScriptTypes.js +149 -0
  90. package/dist/esm/lib/library/NativeCallback.js +59 -0
  91. package/dist/esm/lib/library/NativeLibraryHandle.js +532 -0
  92. package/dist/esm/lib/library/Resolve.js +122 -0
  93. package/dist/esm/lib/thread/NativeThreads.js +377 -0
  94. package/dist/esm/lib/thread/NativeWorker.js +93 -0
  95. package/dist/esm/lib/thread/NativeWorkerCode.js +1 -0
  96. package/dist/esm/lib/thread/PackageRequire.js +2 -0
  97. package/dist/esm/lib/thread/ThreadProtocol.js +1 -0
  98. package/dist/types/components/NativeLibrary.d.ts +229 -0
  99. package/dist/types/decorators/Library.d.ts +61 -0
  100. package/dist/types/decorators/Native.d.ts +69 -0
  101. package/dist/types/exceptions/NativeDeclarationException.d.ts +12 -0
  102. package/dist/types/exceptions/NativeLibraryClosedException.d.ts +12 -0
  103. package/dist/types/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
  104. package/dist/types/exceptions/NativeMemoryException.d.ts +11 -0
  105. package/dist/types/exports/FFI.d.ts +12 -0
  106. package/dist/types/exports/Generator.d.ts +2 -0
  107. package/dist/types/lib/Memory.d.ts +188 -0
  108. package/dist/types/lib/Runtime.d.ts +98 -0
  109. package/dist/types/lib/backend/Functions.d.ts +65 -0
  110. package/dist/types/lib/backend/KoffiTypes.d.ts +53 -0
  111. package/dist/types/lib/declarations/Builtins.d.ts +33 -0
  112. package/dist/types/lib/declarations/Layout.d.ts +41 -0
  113. package/dist/types/lib/declarations/Lexer.d.ts +40 -0
  114. package/dist/types/lib/declarations/Parser.d.ts +90 -0
  115. package/dist/types/lib/declarations/Platform.d.ts +50 -0
  116. package/dist/types/lib/declarations/Types.d.ts +173 -0
  117. package/dist/types/lib/generator/CText.d.ts +14 -0
  118. package/dist/types/lib/generator/Generator.d.ts +136 -0
  119. package/dist/types/lib/generator/Preprocessor.d.ts +60 -0
  120. package/dist/types/lib/generator/TypeScriptTypes.d.ts +35 -0
  121. package/dist/types/lib/library/NativeCallback.d.ts +44 -0
  122. package/dist/types/lib/library/NativeLibraryHandle.d.ts +268 -0
  123. package/dist/types/lib/library/Resolve.d.ts +49 -0
  124. package/dist/types/lib/thread/NativeThreads.d.ts +80 -0
  125. package/dist/types/lib/thread/NativeWorker.d.ts +6 -0
  126. package/dist/types/lib/thread/NativeWorkerCode.d.ts +5 -0
  127. package/dist/types/lib/thread/PackageRequire.d.ts +6 -0
  128. package/dist/types/lib/thread/ThreadProtocol.d.ts +77 -0
  129. 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
+ }