@lakutata/ffi 0.0.0-stage → 3.1.2
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,338 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { Component, DTO, isPackaged, resourcesPath } from '@lakutata/core';
|
|
12
|
+
import { Configurable } from '@lakutata/core/decorator/di';
|
|
13
|
+
import { libraryOptionsOf } from '../decorators/Library.js';
|
|
14
|
+
import { nativeMembersOf } from '../decorators/Native.js';
|
|
15
|
+
import { NativeDeclarationException } from '../exceptions/NativeDeclarationException.js';
|
|
16
|
+
import { NativeLibraryClosedException } from '../exceptions/NativeLibraryClosedException.js';
|
|
17
|
+
import { NativeLibraryHandle } from '../lib/library/NativeLibraryHandle.js';
|
|
18
|
+
/**
|
|
19
|
+
* A native library: a component holding a library loaded with its C declarations (`@Library`), its functions on its
|
|
20
|
+
* properties (`@Native`), its variables (`@NativeVariable`). Write one subclass for each library and register it in the
|
|
21
|
+
* components of the application; it is loaded when it is created, and destroyed with the application: the asynchronous
|
|
22
|
+
* calls running are awaited, its callbacks released, the library unloaded.
|
|
23
|
+
*
|
|
24
|
+
* In a packaged application, the library is also looked for in the directory `native` of the resources (put it there
|
|
25
|
+
* with `lakutata.resources` of the package.json). The exceptions thrown by its persistent callbacks are its `error`
|
|
26
|
+
* events (written to the log of the application when nothing listens to them).
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import {Application} from 'lakutata'
|
|
30
|
+
* import {buildNativeLibraryOptions, Library, Native, NativeLibrary, type Out, type Pointer} from '@lakutata/ffi'
|
|
31
|
+
*
|
|
32
|
+
* @Library({
|
|
33
|
+
* names: {darwin: 'libsqlite3.dylib', linux: 'libsqlite3.so.0', win32: 'winsqlite3.dll'},
|
|
34
|
+
* declarations: `
|
|
35
|
+
* typedef struct sqlite3 sqlite3;
|
|
36
|
+
* #define SQLITE_OK 0
|
|
37
|
+
* `
|
|
38
|
+
* })
|
|
39
|
+
* export class SQLite extends NativeLibrary {
|
|
40
|
+
* @Native('const char *sqlite3_libversion(void)')
|
|
41
|
+
* public readonly libversion: () => string
|
|
42
|
+
*
|
|
43
|
+
* @Native('int sqlite3_open(const char *filename, _Out_ sqlite3 **db)')
|
|
44
|
+
* public readonly open: (filename: string, db: Out<Pointer>) => number
|
|
45
|
+
*
|
|
46
|
+
* @Native('int sqlite3_close(sqlite3 *db)')
|
|
47
|
+
* public readonly close: (db: Pointer) => number
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* Application
|
|
51
|
+
* .run(() => ({
|
|
52
|
+
* id: 'store.app',
|
|
53
|
+
* name: 'Store',
|
|
54
|
+
* components: {
|
|
55
|
+
* sqlite: buildNativeLibraryOptions(SQLite)
|
|
56
|
+
* }
|
|
57
|
+
* }))
|
|
58
|
+
* .onLaunched(async (app: Application): Promise<void> => {
|
|
59
|
+
* const sqlite: SQLite = await app.getObject('sqlite')
|
|
60
|
+
* console.log(sqlite.libversion())
|
|
61
|
+
* })
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class NativeLibrary extends Component {
|
|
65
|
+
#handle;
|
|
66
|
+
/**
|
|
67
|
+
* Load the library and bind its functions and variables to their properties.
|
|
68
|
+
* @protected
|
|
69
|
+
*/
|
|
70
|
+
async init() {
|
|
71
|
+
const library = libraryOptionsOf(this.constructor);
|
|
72
|
+
if (!library)
|
|
73
|
+
throw new NativeDeclarationException(`${this.constructor.name} has no @Library: give the names of its library and its declarations`);
|
|
74
|
+
const resources = isPackaged() ? resourcesPath() : undefined;
|
|
75
|
+
this.#handle = new NativeLibraryHandle(library.names, library.declarations ?? '', {
|
|
76
|
+
path: this.path,
|
|
77
|
+
search: [...this.search ?? [], ...library.search ?? []],
|
|
78
|
+
packaged: resources ? path.join(resources, 'native') : undefined,
|
|
79
|
+
keepLoaded: this.keepLoaded,
|
|
80
|
+
crt: library.crt,
|
|
81
|
+
threads: this.threads ?? library.threads ?? 0,
|
|
82
|
+
onCallbackError: (error) => this.#callbackError(error)
|
|
83
|
+
});
|
|
84
|
+
//The threads have loaded the library (a thread that could not fails the creation)
|
|
85
|
+
try {
|
|
86
|
+
await this.#handle.ready;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
await this.#handle.close(0);
|
|
90
|
+
this.#handle = undefined;
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
for (const [key, member] of nativeMembersOf(Object.getPrototypeOf(this)))
|
|
94
|
+
this.#bindMember(key, member);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Close the library: the asynchronous calls running awaited, the callbacks released, the library unloaded (kept,
|
|
98
|
+
* with its callbacks and its memory, when calls still run after `drainTimeout`); its properties throw from then on.
|
|
99
|
+
* @protected
|
|
100
|
+
*/
|
|
101
|
+
async destroy() {
|
|
102
|
+
const handle = this.#handle;
|
|
103
|
+
if (!handle)
|
|
104
|
+
return;
|
|
105
|
+
for (const [key, member] of nativeMembersOf(Object.getPrototypeOf(this)))
|
|
106
|
+
this.#closeMember(key, member, handle.file);
|
|
107
|
+
await handle.close(this.drainTimeout ?? 10000);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The file the library was loaded from.
|
|
111
|
+
*/
|
|
112
|
+
get file() {
|
|
113
|
+
return this.#library().file;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The asynchronous calls running.
|
|
117
|
+
*/
|
|
118
|
+
get pending() {
|
|
119
|
+
return this.#library().pending;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The worker threads of the library (0: its asynchronous calls run in the thread pool of the runtime).
|
|
123
|
+
*/
|
|
124
|
+
get threadCount() {
|
|
125
|
+
return this.#library().threads;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The constants of the declarations: the values of the enumerations, of the static constants and of the macros
|
|
129
|
+
* whose value is an integer.
|
|
130
|
+
*/
|
|
131
|
+
get constants() {
|
|
132
|
+
return this.#library().constants;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A function of the library bound from its prototype, besides the ones of the properties.
|
|
136
|
+
* @param prototype Its C prototype, with the types of the declarations.
|
|
137
|
+
* @param options Whether it is asynchronous, how its strings are freed, how its 64-bit integers are given.
|
|
138
|
+
* @returns The function.
|
|
139
|
+
*/
|
|
140
|
+
func(prototype, options = {}) {
|
|
141
|
+
return this.#library().func(prototype, options);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A variable the library exports, besides the ones of the properties.
|
|
145
|
+
* @param declaration Its name in the declarations, or its declaration.
|
|
146
|
+
* @returns Its address, and the functions reading and writing its value.
|
|
147
|
+
*/
|
|
148
|
+
variable(declaration) {
|
|
149
|
+
return this.#library().variable(declaration);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A persistent callback: a JS function the library can call at any time, released by `release()` or when the
|
|
153
|
+
* component is destroyed. Its exceptions are `error` events of the component.
|
|
154
|
+
* @param type The name of a pointer-to-function type of the declarations, or its prototype.
|
|
155
|
+
* @param callback The JS function.
|
|
156
|
+
* @returns The callback, whose `pointer` is given to the library.
|
|
157
|
+
*/
|
|
158
|
+
callback(type, callback) {
|
|
159
|
+
return this.#library().callback(type, callback);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Read native memory as a type of the declarations: a value, or an array of values.
|
|
163
|
+
* @param pointer The address.
|
|
164
|
+
* @param type The type: `int`, `struct timeval`, `char *` (a string).
|
|
165
|
+
* @param count The number of values, for an array.
|
|
166
|
+
* @returns The value, or the array.
|
|
167
|
+
*/
|
|
168
|
+
read(pointer, type, count) {
|
|
169
|
+
return this.#library().read(pointer, type, count);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Write a value to native memory as a type of the declarations.
|
|
173
|
+
* @param pointer The address.
|
|
174
|
+
* @param type The type.
|
|
175
|
+
* @param value The value.
|
|
176
|
+
*/
|
|
177
|
+
write(pointer, type, value) {
|
|
178
|
+
this.#library().write(pointer, type, value);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The size of a type of the declarations, in bytes.
|
|
182
|
+
* @param type The type.
|
|
183
|
+
*/
|
|
184
|
+
sizeOf(type) {
|
|
185
|
+
return this.#library().sizeOf(type);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The offset of a member of a struct of the declarations, in bytes.
|
|
189
|
+
* @param type The struct.
|
|
190
|
+
* @param member The member.
|
|
191
|
+
*/
|
|
192
|
+
offsetOf(type, member) {
|
|
193
|
+
return this.#library().offsetOf(type, member);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Allocate native memory for values of a type of the declarations, which the library can keep; freed by `free()`,
|
|
197
|
+
* or when the component is destroyed.
|
|
198
|
+
* @param type The type.
|
|
199
|
+
* @param count The number of values.
|
|
200
|
+
* @returns Its address.
|
|
201
|
+
*/
|
|
202
|
+
alloc(type, count = 1) {
|
|
203
|
+
return this.#library().alloc(type, count);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Free memory allocated by `alloc()`.
|
|
207
|
+
* @param pointer Its address.
|
|
208
|
+
*/
|
|
209
|
+
free(pointer) {
|
|
210
|
+
this.#library().free(pointer);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The errno of the last call of this thread (on Windows, the one of the C library of `@Library`, ucrtbase.dll by
|
|
214
|
+
* default); with threads, the one of the thread of the last asynchronous call completed.
|
|
215
|
+
*/
|
|
216
|
+
errno() {
|
|
217
|
+
return this.#library().errno();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The last error of the Windows API after the last call of this thread; with threads, the one of the thread of the
|
|
221
|
+
* last asynchronous call completed; undefined on the other systems.
|
|
222
|
+
*/
|
|
223
|
+
windowsError() {
|
|
224
|
+
return this.#library().windowsError();
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The library, once loaded
|
|
228
|
+
*/
|
|
229
|
+
#library() {
|
|
230
|
+
if (!this.#handle)
|
|
231
|
+
throw new NativeLibraryClosedException(`The library of ${this.constructor.name} is not loaded`);
|
|
232
|
+
return this.#handle;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* A function or a variable bound to its property
|
|
236
|
+
* @param key
|
|
237
|
+
* @param member
|
|
238
|
+
*/
|
|
239
|
+
#bindMember(key, member) {
|
|
240
|
+
const handle = this.#handle;
|
|
241
|
+
const declaration = member.declaration ?? key;
|
|
242
|
+
const named = /^[A-Za-z_]\w*$/.test(declaration);
|
|
243
|
+
if (member.kind === 'function') {
|
|
244
|
+
const bound = named ? handle.function(declaration, member.options) : handle.func(declaration, member.options);
|
|
245
|
+
Object.defineProperty(this, key, { value: bound, writable: false, enumerable: true, configurable: true });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const variable = handle.variable(declaration);
|
|
249
|
+
Object.defineProperty(this, key, {
|
|
250
|
+
get: () => variable.get(),
|
|
251
|
+
set: (value) => variable.set(value),
|
|
252
|
+
enumerable: true,
|
|
253
|
+
configurable: true
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A property of a closed library: throwing (an asynchronous function rejecting)
|
|
258
|
+
* @param key
|
|
259
|
+
* @param member
|
|
260
|
+
* @param file
|
|
261
|
+
*/
|
|
262
|
+
#closeMember(key, member, file) {
|
|
263
|
+
const closed = () => new NativeLibraryClosedException(`The library ${file} is closed: ${this.constructor.name}.${key} cannot be used`);
|
|
264
|
+
if (member.kind === 'function') {
|
|
265
|
+
const value = member.options.async ? () => Promise.reject(closed()) : () => {
|
|
266
|
+
throw closed();
|
|
267
|
+
};
|
|
268
|
+
Object.defineProperty(this, key, { value: value, writable: false, enumerable: true, configurable: true });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const fail = () => {
|
|
272
|
+
throw closed();
|
|
273
|
+
};
|
|
274
|
+
Object.defineProperty(this, key, { get: fail, set: fail, enumerable: true, configurable: true });
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* An exception of a persistent callback: an error event, or the log of the application when nothing listens
|
|
278
|
+
* @param error
|
|
279
|
+
*/
|
|
280
|
+
#callbackError(error) {
|
|
281
|
+
if (this.listenerCount('error')) {
|
|
282
|
+
this.emit('error', error);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const message = `A callback of the native library ${this.#handle?.file ?? this.constructor.name} threw`;
|
|
286
|
+
this.getModule().getObject('log').then((log) => log.error(error, message), () => console.error(`${message}:`, error));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
__decorate([
|
|
290
|
+
Configurable(DTO.String().optional()),
|
|
291
|
+
__metadata("design:type", String)
|
|
292
|
+
], NativeLibrary.prototype, "path", void 0);
|
|
293
|
+
__decorate([
|
|
294
|
+
Configurable(DTO.Array().items(DTO.String()).optional()),
|
|
295
|
+
__metadata("design:type", Array)
|
|
296
|
+
], NativeLibrary.prototype, "search", void 0);
|
|
297
|
+
__decorate([
|
|
298
|
+
Configurable(DTO.Boolean().optional()),
|
|
299
|
+
__metadata("design:type", Boolean)
|
|
300
|
+
], NativeLibrary.prototype, "keepLoaded", void 0);
|
|
301
|
+
__decorate([
|
|
302
|
+
Configurable(DTO.Number().min(0).optional()),
|
|
303
|
+
__metadata("design:type", Number)
|
|
304
|
+
], NativeLibrary.prototype, "drainTimeout", void 0);
|
|
305
|
+
__decorate([
|
|
306
|
+
Configurable(DTO.Number().integer().min(0).optional()),
|
|
307
|
+
__metadata("design:type", Number)
|
|
308
|
+
], NativeLibrary.prototype, "threads", void 0);
|
|
309
|
+
/**
|
|
310
|
+
* Build the declaration of a library component, for the `components` of an application.
|
|
311
|
+
* @param library The `NativeLibrary` subclass of the library.
|
|
312
|
+
* @param options Where it is found (a file, directories searched first), whether it stays loaded, how long its
|
|
313
|
+
* destruction waits for its asynchronous calls.
|
|
314
|
+
* @returns The component declaration.
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* import {Application} from 'lakutata'
|
|
318
|
+
* import {buildNativeLibraryOptions, Library, Native, NativeLibrary} from '@lakutata/ffi'
|
|
319
|
+
*
|
|
320
|
+
* @Library({names: 'vendor'})
|
|
321
|
+
* class Vendor extends NativeLibrary {
|
|
322
|
+
* @Native('int vendor_version(void)')
|
|
323
|
+
* public readonly version: () => number
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* Application.run(() => ({
|
|
327
|
+
* id: 'devices.app',
|
|
328
|
+
* name: 'Devices',
|
|
329
|
+
* components: {
|
|
330
|
+
* //The library shipped with the application: an alias, or a path relative to the working directory
|
|
331
|
+
* vendor: buildNativeLibraryOptions(Vendor, {search: ['@data/native']})
|
|
332
|
+
* }
|
|
333
|
+
* }))
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
export function buildNativeLibraryOptions(library, options = {}) {
|
|
337
|
+
return { class: library, ...options };
|
|
338
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const libraries = new WeakMap();
|
|
2
|
+
/**
|
|
3
|
+
* Declare a native library on a `NativeLibrary` subclass: its file names, the directories it is looked for in, its C
|
|
4
|
+
* declarations. Its functions are declared on its properties by `@Native`, its variables by `@NativeVariable`.
|
|
5
|
+
* @param options Its names, its search directories, its declarations.
|
|
6
|
+
* @returns The class decorator.
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {Library, Native, NativeLibrary} from '@lakutata/ffi'
|
|
10
|
+
*
|
|
11
|
+
* @Library({
|
|
12
|
+
* names: {darwin: 'libz.dylib', linux: 'libz.so.1', win32: 'zlib1.dll'},
|
|
13
|
+
* declarations: 'typedef unsigned long uLong;'
|
|
14
|
+
* })
|
|
15
|
+
* export class Zlib extends NativeLibrary {
|
|
16
|
+
* @Native('const char *zlibVersion(void)')
|
|
17
|
+
* public readonly zlibVersion: () => string
|
|
18
|
+
*
|
|
19
|
+
* @Native('uLong crc32(uLong crc, const unsigned char *buffer, unsigned int length)')
|
|
20
|
+
* public readonly crc32: (crc: bigint | number, buffer: Buffer, length: number) => bigint
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function Library(options) {
|
|
25
|
+
return (target) => {
|
|
26
|
+
libraries.set(target, options);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The library declared on a class or on the nearest of its parent classes
|
|
31
|
+
* @param target
|
|
32
|
+
*/
|
|
33
|
+
export function libraryOptionsOf(target) {
|
|
34
|
+
for (let current = target; current && current !== Function.prototype; current = Object.getPrototypeOf(current)) {
|
|
35
|
+
const options = libraries.get(current);
|
|
36
|
+
if (options)
|
|
37
|
+
return options;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const members = new WeakMap();
|
|
2
|
+
/**
|
|
3
|
+
* Record a member on the prototype of a class
|
|
4
|
+
* @param target
|
|
5
|
+
* @param key
|
|
6
|
+
* @param member
|
|
7
|
+
*/
|
|
8
|
+
function record(target, key, member) {
|
|
9
|
+
if (typeof key !== 'string')
|
|
10
|
+
throw new TypeError('A native function or variable is declared on a property with a string name');
|
|
11
|
+
let own = members.get(target);
|
|
12
|
+
if (!own)
|
|
13
|
+
members.set(target, own = new Map());
|
|
14
|
+
own.set(key, member);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Declare a function of the library on a property of a `NativeLibrary` subclass: the property gets the function once
|
|
18
|
+
* the component is created (the function of koffi itself when nothing is converted), and a function throwing once it
|
|
19
|
+
* is destroyed. The TypeScript type of the property is the one of the function.
|
|
20
|
+
* @param declaration Its C prototype (`int sqlite3_open(const char *filename, _Out_ sqlite3 **db)`), or the name of a
|
|
21
|
+
* function of the declarations of the library; by default the function of the declarations named as the property.
|
|
22
|
+
* @param options Whether it runs in a thread of the pool (returning a promise), the function freeing the strings it
|
|
23
|
+
* gives, its 64-bit integers as numbers.
|
|
24
|
+
* @returns The property decorator.
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import {Library, Native, NativeLibrary, type Out, type Pointer} from '@lakutata/ffi'
|
|
28
|
+
*
|
|
29
|
+
* @Library({
|
|
30
|
+
* names: {darwin: 'libsqlite3.dylib', linux: 'libsqlite3.so.0', win32: 'winsqlite3.dll'},
|
|
31
|
+
* declarations: `
|
|
32
|
+
* typedef struct sqlite3 sqlite3;
|
|
33
|
+
* int sqlite3_close(sqlite3 *db);
|
|
34
|
+
* `
|
|
35
|
+
* })
|
|
36
|
+
* export class SQLite extends NativeLibrary {
|
|
37
|
+
* @Native('int sqlite3_open(const char *filename, _Out_ sqlite3 **db)')
|
|
38
|
+
* public readonly open: (filename: string, db: Out<Pointer>) => number
|
|
39
|
+
*
|
|
40
|
+
* //The function of the declarations named as the property
|
|
41
|
+
* @Native()
|
|
42
|
+
* public readonly sqlite3_close: (db: Pointer) => number
|
|
43
|
+
*
|
|
44
|
+
* @Native('int sqlite3_exec(sqlite3 *db, const char *sql, void *callback, void *context, _Out_ char **error)', {async: true, free: 'sqlite3_free'})
|
|
45
|
+
* public readonly exec: (db: Pointer, sql: string, callback: null, context: null, error: Out<string>) => Promise<number>
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function Native(declaration, options = {}) {
|
|
50
|
+
return (target, key) => record(target, key, { kind: 'function', declaration: declaration, options: options });
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Declare a variable the library exports on a property of a `NativeLibrary` subclass: reading the property reads the
|
|
54
|
+
* variable, writing it writes the variable (refused for a const one).
|
|
55
|
+
* @param declaration Its declaration (`extern const char sqlite3_version[]`), or the name of a variable of the
|
|
56
|
+
* declarations of the library; by default the variable of the declarations named as the property.
|
|
57
|
+
* @returns The property decorator.
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* import {Library, NativeLibrary, NativeVariable} from '@lakutata/ffi'
|
|
61
|
+
*
|
|
62
|
+
* @Library({names: {darwin: 'libsqlite3.dylib', linux: 'libsqlite3.so.0', win32: 'winsqlite3.dll'}})
|
|
63
|
+
* export class SQLiteVersion extends NativeLibrary {
|
|
64
|
+
* @NativeVariable('extern const char sqlite3_version[]')
|
|
65
|
+
* public readonly version: string
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function NativeVariable(declaration) {
|
|
70
|
+
return (target, key) => record(target, key, { kind: 'variable', declaration: declaration });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The native members of a class and of its parent classes (a subclass replacing a member of its parent)
|
|
74
|
+
* @param prototype
|
|
75
|
+
*/
|
|
76
|
+
export function nativeMembersOf(prototype) {
|
|
77
|
+
const chain = [];
|
|
78
|
+
for (let current = prototype; current && current !== Object.prototype; current = Object.getPrototypeOf(current))
|
|
79
|
+
chain.unshift(current);
|
|
80
|
+
const all = new Map();
|
|
81
|
+
for (const current of chain)
|
|
82
|
+
for (const [key, member] of members.get(current) ?? [])
|
|
83
|
+
all.set(key, member);
|
|
84
|
+
return all;
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Exception } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when C declarations or a function prototype cannot be read: its message gives the line and the column, what
|
|
4
|
+
* was found and what was expected (a type not declared, a construct not supported such as a macro or `long double`).
|
|
5
|
+
* Its `errno` is `'E_NATIVE_DECLARATION'`.
|
|
6
|
+
*/
|
|
7
|
+
export class NativeDeclarationException extends Exception {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
/**
|
|
11
|
+
* `'E_NATIVE_DECLARATION'`.
|
|
12
|
+
*/
|
|
13
|
+
this.errno = 'E_NATIVE_DECLARATION';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Exception } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a function, a variable or a callback of a library is used once the library is closed (its component
|
|
4
|
+
* destroyed, or `close()` called): calling a function of an unloaded library would end the process. Its `errno` is
|
|
5
|
+
* `'E_NATIVE_LIBRARY_CLOSED'`.
|
|
6
|
+
*/
|
|
7
|
+
export class NativeLibraryClosedException extends Exception {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
/**
|
|
11
|
+
* `'E_NATIVE_LIBRARY_CLOSED'`.
|
|
12
|
+
*/
|
|
13
|
+
this.errno = 'E_NATIVE_LIBRARY_CLOSED';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Exception } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a library cannot be loaded: its message lists each file tried and why it could not be loaded (not found,
|
|
4
|
+
* built for another architecture, a library it needs missing). Its `errno` is `'E_NATIVE_LIBRARY_NOT_FOUND'`.
|
|
5
|
+
*/
|
|
6
|
+
export class NativeLibraryNotFoundException extends Exception {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
/**
|
|
10
|
+
* `'E_NATIVE_LIBRARY_NOT_FOUND'`.
|
|
11
|
+
*/
|
|
12
|
+
this.errno = 'E_NATIVE_LIBRARY_NOT_FOUND';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Exception } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when native memory cannot be read or written as asked: a null pointer, a negative length, a view of native
|
|
4
|
+
* memory where the runtime forbids it (Electron). Its `errno` is `'E_NATIVE_MEMORY'`.
|
|
5
|
+
*/
|
|
6
|
+
export class NativeMemoryException extends Exception {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
/**
|
|
10
|
+
* `'E_NATIVE_MEMORY'`.
|
|
11
|
+
*/
|
|
12
|
+
this.errno = 'E_NATIVE_MEMORY';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { NativeDeclarationException } from '../exceptions/NativeDeclarationException.js';
|
|
2
|
+
export { NativeLibraryNotFoundException } from '../exceptions/NativeLibraryNotFoundException.js';
|
|
3
|
+
export { NativeLibraryClosedException } from '../exceptions/NativeLibraryClosedException.js';
|
|
4
|
+
export { NativeMemoryException } from '../exceptions/NativeMemoryException.js';
|
|
5
|
+
export { NativeCallback } from '../lib/library/NativeCallback.js';
|
|
6
|
+
export { loadNativeLibrary, NativeLibraryHandle } from '../lib/library/NativeLibraryHandle.js';
|
|
7
|
+
export { allocMemory, freeMemory, lastErrno, lastWindowsError, readArray, readBuffer, readCString, readValue, readWString, sizeOf, viewBuffer, writeValue } from '../lib/Memory.js';
|
|
8
|
+
export { Library } from '../decorators/Library.js';
|
|
9
|
+
export { Native, NativeVariable } from '../decorators/Native.js';
|
|
10
|
+
export { buildNativeLibraryOptions, NativeLibrary } from '../components/NativeLibrary.js';
|