@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.
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,426 @@
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.KoffiTypes = void 0;
7
+ exports.toBigInt = toBigInt;
8
+ const koffi_1 = __importDefault(require("koffi"));
9
+ const NativeDeclarationException_js_1 = require("../../exceptions/NativeDeclarationException.js");
10
+ const Layout_js_1 = require("../declarations/Layout.js");
11
+ const Types_js_1 = require("../declarations/Types.js");
12
+ /**
13
+ * The names koffi gives the arithmetic types (ssize_t and ptrdiff_t have the size and the sign of intptr_t)
14
+ */
15
+ const KOFFI_NAMES = {
16
+ void: 'void',
17
+ bool: 'bool',
18
+ char: 'char',
19
+ schar: 'int8_t',
20
+ uchar: 'uchar',
21
+ short: 'short',
22
+ ushort: 'ushort',
23
+ int: 'int',
24
+ uint: 'uint',
25
+ long: 'long',
26
+ ulong: 'ulong',
27
+ longlong: 'longlong',
28
+ ulonglong: 'ulonglong',
29
+ int8: 'int8_t',
30
+ uint8: 'uint8_t',
31
+ int16: 'int16_t',
32
+ uint16: 'uint16_t',
33
+ int32: 'int32_t',
34
+ uint32: 'uint32_t',
35
+ int64: 'int64_t',
36
+ uint64: 'uint64_t',
37
+ size: 'size_t',
38
+ ssize: 'intptr_t',
39
+ intptr: 'intptr_t',
40
+ uintptr: 'uintptr_t',
41
+ ptrdiff: 'intptr_t',
42
+ char16: 'char16_t',
43
+ char32: 'char32_t',
44
+ wchar: 'wchar_t',
45
+ float: 'float',
46
+ double: 'double'
47
+ };
48
+ /**
49
+ * The prefix of the members the package adds to the structs it lays out itself (the bytes of the bitfields, padding):
50
+ * the names starting with two underscores are reserved to the compilers and the C library, never a struct of a user
51
+ */
52
+ const INTERNAL = '__lakutata_';
53
+ /**
54
+ * A value of a 64-bit integer type, as a bigint
55
+ * @param value
56
+ */
57
+ function toBigInt(value) {
58
+ return typeof value === 'number' ? BigInt(value) : value;
59
+ }
60
+ /**
61
+ * The integer type of a primitive or of an enumeration
62
+ * @param type
63
+ */
64
+ function integerName(type) {
65
+ if (type.kind === 'primitive')
66
+ return type.name;
67
+ if (type.kind === 'enum')
68
+ return type.base;
69
+ return undefined;
70
+ }
71
+ /**
72
+ * Round up to a multiple
73
+ * @param value
74
+ * @param multiple
75
+ */
76
+ function alignUp(value, multiple) {
77
+ return Math.ceil(value / multiple) * multiple;
78
+ }
79
+ /**
80
+ * The koffi types of the C types of the declarations, made once each (koffi never frees a type): anonymous, so the
81
+ * types of two libraries never meet; the structs koffi cannot lay out (bitfields, packing) laid out by the package
82
+ */
83
+ class KoffiTypes {
84
+ #platform;
85
+ #types = new WeakMap();
86
+ #converters = new WeakMap();
87
+ #prototypes = new WeakMap();
88
+ /**
89
+ * The koffi types of a platform.
90
+ * @param platform The platform of the process.
91
+ */
92
+ constructor(platform) {
93
+ this.#platform = platform;
94
+ }
95
+ /**
96
+ * The koffi type of a C type, as a value: a member, a result, a parameter without direction.
97
+ * @param type The C type.
98
+ */
99
+ type(type) {
100
+ switch (type.kind) {
101
+ case 'primitive':
102
+ return KOFFI_NAMES[type.name];
103
+ case 'enum':
104
+ return KOFFI_NAMES[type.base];
105
+ case 'pointer':
106
+ return this.#pointer(type.to);
107
+ case 'array':
108
+ return this.#cached(type, () => koffi_1.default.array(this.type(type.of), type.length));
109
+ case 'struct':
110
+ case 'union':
111
+ if (type.tag === 'va_list' && !type.members)
112
+ throw new NativeDeclarationException_js_1.NativeDeclarationException('A va_list cannot be passed: call the function of the library that takes the arguments themselves');
113
+ return this.#record(type);
114
+ case 'function':
115
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`A function is not a value: use a pointer to it (${(0, Types_js_1.typeText)(type)})`);
116
+ }
117
+ }
118
+ /**
119
+ * The koffi prototype of a function type: the type of its callbacks.
120
+ * @param type The function type.
121
+ */
122
+ prototype(type) {
123
+ const known = this.#prototypes.get(type);
124
+ if (known)
125
+ return known;
126
+ if (type.variadic)
127
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`A callback cannot be variadic: ${(0, Types_js_1.typeText)(type)}`);
128
+ const prototype = koffi_1.default.proto(`__${type.convention}`, this.type(type.result), type.parameters.map((parameter) => this.type(parameter.type)));
129
+ this.#prototypes.set(type, prototype);
130
+ return prototype;
131
+ }
132
+ /**
133
+ * The converter of a C type, null when koffi gives and takes its values as the package does.
134
+ * @param type The C type.
135
+ */
136
+ converter(type) {
137
+ const known = this.#converters.get(type);
138
+ if (known !== undefined)
139
+ return known;
140
+ let converter = null;
141
+ switch (type.kind) {
142
+ case 'primitive':
143
+ case 'enum':
144
+ if ((0, Types_js_1.primitiveBig)(integerName(type)))
145
+ converter = { fromNative: toBigInt };
146
+ break;
147
+ case 'array': {
148
+ const element = this.converter(type.of);
149
+ if (element) {
150
+ converter = {
151
+ ...element.toNative ? { toNative: (value) => Array.isArray(value) ? value.map(element.toNative) : value } : {},
152
+ ...element.fromNative ? { fromNative: (value) => value && typeof value !== 'string' ? Array.from(value, element.fromNative) : value } : {}
153
+ };
154
+ }
155
+ break;
156
+ }
157
+ case 'struct':
158
+ case 'union':
159
+ if (type.members) {
160
+ //Reserved first: a struct reached through its own members converts as it is being made
161
+ this.#converters.set(type, null);
162
+ converter = this.#recordConverter(type);
163
+ }
164
+ break;
165
+ }
166
+ this.#converters.set(type, converter);
167
+ return converter;
168
+ }
169
+ /**
170
+ * The koffi type of a pointer: the strings for char (and char16_t, wchar_t), the prototypes for functions
171
+ * @param to
172
+ */
173
+ #pointer(to) {
174
+ if (to.kind === 'primitive') {
175
+ if (to.name === 'char')
176
+ return 'char *';
177
+ if (to.name === 'char16')
178
+ return 'char16_t *';
179
+ if (to.name === 'wchar')
180
+ return 'wchar_t *';
181
+ if (to.name === 'void')
182
+ return 'void *';
183
+ }
184
+ if (to.kind === 'function')
185
+ return this.#cached(to, () => koffi_1.default.pointer(this.prototype(to)));
186
+ if ((to.kind === 'struct' || to.kind === 'union') && !to.members)
187
+ return this.#cached(to, () => koffi_1.default.pointer(koffi_1.default.opaque()));
188
+ return koffi_1.default.pointer(this.type(to));
189
+ }
190
+ /**
191
+ * A type made once for a C type
192
+ * @param type
193
+ * @param make
194
+ */
195
+ #cached(type, make) {
196
+ let made = this.#types.get(type);
197
+ if (!made) {
198
+ made = make();
199
+ this.#types.set(type, made);
200
+ }
201
+ return made;
202
+ }
203
+ /**
204
+ * The koffi type of a struct or a union, declared first as an opaque type its own members can point to
205
+ * @param record
206
+ */
207
+ #record(record) {
208
+ const known = this.#types.get(record);
209
+ if (known)
210
+ return known;
211
+ const layout = (0, Layout_js_1.layoutOf)(record, this.#platform);
212
+ const placeholder = koffi_1.default.opaque();
213
+ this.#types.set(record, placeholder);
214
+ const members = this.#members(record, layout);
215
+ const made = record.kind === 'union' ? koffi_1.default.union(placeholder, members) : koffi_1.default.struct(placeholder, members);
216
+ this.#types.set(record, made);
217
+ //What koffi lays out must be what the platform lays out: the size, the alignment, the offset of each member
218
+ const misplaced = record.kind === 'struct' ? layout.members.find((placed) => placed.bits === undefined && !!made.members?.[placed.member.name] && made.members[placed.member.name].offset !== placed.offset) : undefined;
219
+ if (made.size !== layout.size || made.alignment !== layout.alignment || misplaced) {
220
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The layout of ${(0, Types_js_1.typeText)(record)} could not be given to koffi (${made.size} bytes aligned on ${made.alignment}, ${layout.size} on ${layout.alignment} expected${misplaced ? `, ${misplaced.member.name} at ${made.members[misplaced.member.name].offset}, ${misplaced.offset} expected` : ''})`);
221
+ }
222
+ return made;
223
+ }
224
+ /**
225
+ * The members of a struct as koffi lays them out: each at the alignment the platform gives it (capped by the packing,
226
+ * raised by an aligned attribute), the bytes of the bitfields and the padding they need as arrays of bytes, the
227
+ * alignment of the struct on its first member when its members alone do not give it
228
+ * @param record
229
+ * @param layout
230
+ */
231
+ #members(record, layout) {
232
+ const entries = [];
233
+ if (record.kind === 'union') {
234
+ for (const placed of layout.members)
235
+ entries.push({ name: placed.member.name, type: this.type(placed.member.type), offset: 0, size: placed.size, alignment: this.#memberAlignment(placed.member, record) });
236
+ }
237
+ else {
238
+ let chunk = 0;
239
+ for (const group of this.#groups(layout)) {
240
+ if ('bitfields' in group) {
241
+ entries.push({ name: `${INTERNAL}bits${chunk++}`, type: koffi_1.default.array('uint8_t', group.size, 'Typed'), offset: group.offset, size: group.size, alignment: 1 });
242
+ continue;
243
+ }
244
+ //A flexible array member has no bytes: koffi does not need it
245
+ if (group.member.type.kind === 'array' && group.member.type.length === 0)
246
+ continue;
247
+ entries.push({ name: group.member.name, type: this.type(group.member.type), offset: group.offset, size: group.size, alignment: this.#memberAlignment(group.member, record) });
248
+ }
249
+ }
250
+ const members = {};
251
+ let end = 0;
252
+ let alignment = 1;
253
+ let padding = 0;
254
+ entries.forEach((entry, index) => {
255
+ if (record.kind === 'struct' && alignUp(end, entry.alignment) !== entry.offset) {
256
+ members[`${INTERNAL}pad${padding++}`] = [1, koffi_1.default.array('uint8_t', entry.offset - end, 'Typed')];
257
+ end = entry.offset;
258
+ }
259
+ //The alignment of the struct, on its first member (at 0, aligned on anything)
260
+ const own = index === 0 && layout.alignment > entry.alignment && this.#alignmentShortfall(entries, layout) ? layout.alignment : entry.alignment;
261
+ members[entry.name] = own === this.#naturalAlignment(entry.type) ? entry.type : [own, entry.type];
262
+ alignment = Math.max(alignment, own);
263
+ end = record.kind === 'struct' ? entry.offset + entry.size : Math.max(end, entry.size);
264
+ });
265
+ if (!entries.length)
266
+ members[`${INTERNAL}pad${padding++}`] = [layout.alignment, koffi_1.default.array('uint8_t', layout.size || 1, 'Typed')];
267
+ else if (record.kind === 'struct' && alignUp(end, Math.max(alignment, layout.alignment)) < layout.size)
268
+ members[`${INTERNAL}pad${padding++}`] = [1, koffi_1.default.array('uint8_t', layout.size - end, 'Typed')];
269
+ return members;
270
+ }
271
+ /**
272
+ * Whether the members of a struct alone are aligned less than the struct (an aligned attribute on the struct, the
273
+ * type of a bitfield, a flexible array member)
274
+ * @param entries
275
+ * @param layout
276
+ */
277
+ #alignmentShortfall(entries, layout) {
278
+ return Math.max(1, ...entries.map((entry) => entry.alignment)) < layout.alignment;
279
+ }
280
+ /**
281
+ * The alignment koffi gives a type by itself
282
+ * @param type
283
+ */
284
+ #naturalAlignment(type) {
285
+ return koffi_1.default.type(type).alignment;
286
+ }
287
+ /**
288
+ * The alignment of a member on the platform: its type's, raised by its aligned attribute, capped by the packing
289
+ * @param member
290
+ * @param record
291
+ */
292
+ #memberAlignment(member, record) {
293
+ const natural = Math.max((0, Layout_js_1.alignmentOf)(member.type, this.#platform), member.align ?? 1);
294
+ return record.pack ? Math.min(natural, record.pack) : natural;
295
+ }
296
+ /**
297
+ * The members of a struct in the order of their offsets, the bitfields sharing bytes grouped
298
+ * @param layout
299
+ */
300
+ #groups(layout) {
301
+ const groups = [];
302
+ for (const placed of layout.members) {
303
+ if (placed.bits === undefined) {
304
+ groups.push({ member: placed.member, offset: placed.offset, size: placed.size });
305
+ continue;
306
+ }
307
+ const first = Math.floor(placed.bitOffset / 8);
308
+ const last = Math.ceil((placed.bitOffset + placed.bits) / 8);
309
+ const previous = groups[groups.length - 1];
310
+ if (previous && 'bitfields' in previous && first <= previous.offset + previous.size) {
311
+ previous.bitfields.push(placed);
312
+ previous.size = Math.max(previous.size, last - previous.offset);
313
+ }
314
+ else {
315
+ groups.push({ bitfields: [placed], offset: first, size: last - first });
316
+ }
317
+ }
318
+ return groups;
319
+ }
320
+ /**
321
+ * The converter of a struct or a union: the converters of its members; its bitfields read from and written to the
322
+ * bytes holding them; the members the package added left out
323
+ * @param record
324
+ */
325
+ #recordConverter(record) {
326
+ const layout = (0, Layout_js_1.layoutOf)(record, this.#platform);
327
+ const groups = record.kind === 'struct' ? this.#groups(layout) : layout.members.map((placed) => ({ member: placed.member, offset: 0, size: placed.size }));
328
+ const made = this.#record(record);
329
+ const internal = Object.keys(made.members ?? {}).some((name) => name.startsWith(INTERNAL));
330
+ const members = [];
331
+ const bitfields = [];
332
+ let chunk = 0;
333
+ for (const group of groups) {
334
+ if ('bitfields' in group) {
335
+ const name = `${INTERNAL}bits${chunk++}`;
336
+ for (const placed of group.bitfields) {
337
+ if (!placed.member.name)
338
+ continue;
339
+ const integer = integerName(placed.member.type);
340
+ bitfields.push({ name: placed.member.name, chunk: name, offset: placed.bitOffset - group.offset * 8, bits: placed.bits, signed: (0, Types_js_1.primitiveSigned)(integer, this.#platform), big: (0, Types_js_1.primitiveBig)(integer) });
341
+ }
342
+ continue;
343
+ }
344
+ if (group.member.type.kind === 'array' && group.member.type.length === 0)
345
+ continue;
346
+ members.push({ name: group.member.name, converter: this.converter(group.member.type) });
347
+ }
348
+ const converted = members.filter((member) => !!member.converter);
349
+ if (!converted.length && !bitfields.length && !internal)
350
+ return null;
351
+ const chunks = new Map(groups.filter((group) => 'bitfields' in group).map((group, index) => [`${INTERNAL}bits${index}`, group.size]));
352
+ const toNativeMembers = converted.filter((member) => !!member.converter.toNative);
353
+ const writes = toNativeMembers.length > 0 || bitfields.length > 0;
354
+ return {
355
+ ...writes ? {
356
+ toNative: (value) => {
357
+ if (!value || typeof value !== 'object' || ArrayBuffer.isView(value))
358
+ return value;
359
+ const native = { ...value };
360
+ for (const member of toNativeMembers)
361
+ if (member.name in native)
362
+ native[member.name] = member.converter.toNative(native[member.name]);
363
+ if (bitfields.length) {
364
+ const bytes = new Map([...chunks].map(([name, size]) => [name, new Uint8Array(size)]));
365
+ for (const field of bitfields) {
366
+ const given = native[field.name];
367
+ delete native[field.name];
368
+ if (given === undefined || given === null)
369
+ continue;
370
+ writeBits(bytes.get(field.chunk), field.offset, field.bits, BigInt(given));
371
+ }
372
+ for (const [name, array] of bytes)
373
+ native[name] = array;
374
+ }
375
+ return native;
376
+ }
377
+ } : {},
378
+ fromNative: (value) => {
379
+ if (!value || typeof value !== 'object')
380
+ return value;
381
+ const result = {};
382
+ for (const member of members)
383
+ if (member.name in value)
384
+ result[member.name] = member.converter?.fromNative ? member.converter.fromNative(value[member.name]) : value[member.name];
385
+ for (const field of bitfields) {
386
+ const bits = readBits(value[field.chunk], field.offset, field.bits, field.signed);
387
+ result[field.name] = field.big ? bits : Number(bits);
388
+ }
389
+ return result;
390
+ }
391
+ };
392
+ }
393
+ }
394
+ exports.KoffiTypes = KoffiTypes;
395
+ /**
396
+ * The bits of a bitfield, from its bytes (the lowest bits first)
397
+ * @param bytes
398
+ * @param offset its first bit in the bytes
399
+ * @param bits its width
400
+ * @param signed whether its type is signed (its highest bit extended)
401
+ */
402
+ function readBits(bytes, offset, bits, signed) {
403
+ let value = 0n;
404
+ for (let i = bits - 1; i >= 0; i--) {
405
+ const bit = offset + i;
406
+ value = (value << 1n) | BigInt((bytes[bit >> 3] >> (bit & 7)) & 1);
407
+ }
408
+ return signed ? BigInt.asIntN(bits, value) : value;
409
+ }
410
+ /**
411
+ * The bits of a bitfield written to its bytes
412
+ * @param bytes
413
+ * @param offset
414
+ * @param bits
415
+ * @param value
416
+ */
417
+ function writeBits(bytes, offset, bits, value) {
418
+ const unsigned = BigInt.asUintN(bits, value);
419
+ for (let i = 0; i < bits; i++) {
420
+ const bit = offset + i;
421
+ if ((unsigned >> BigInt(i)) & 1n)
422
+ bytes[bit >> 3] |= 1 << (bit & 7);
423
+ else
424
+ bytes[bit >> 3] &= ~(1 << (bit & 7));
425
+ }
426
+ }
@@ -0,0 +1,33 @@
1
+ import type { Platform } from './Platform.js';
2
+ import type { CallingConvention, CType, Direction } from './Types.js';
3
+ /**
4
+ * The types every declaration knows without declaring them: the ones of stdint.h and stddef.h, the ones of POSIX with
5
+ * their size on the platform, the ones of the Windows API (the same on every platform: DWORD is 32-bit everywhere).
6
+ * A declaration of the same name replaces them
7
+ * @param platform
8
+ */
9
+ export declare function builtinTypes(platform: Platform): Map<string, CType>;
10
+ /**
11
+ * The words naming a calling convention: the keywords of the compilers, the macros of the Windows API
12
+ */
13
+ export declare const CONVENTIONS: ReadonlyMap<string, CallingConvention>;
14
+ /**
15
+ * The words read and ignored where qualifiers go: the qualifiers that do not change how a value is passed, the macros of
16
+ * the Windows API that are empty
17
+ */
18
+ export declare const IGNORED: ReadonlySet<string>;
19
+ /**
20
+ * The words read as const
21
+ */
22
+ export declare const CONSTS: ReadonlySet<string>;
23
+ /**
24
+ * The words of inline functions, whose bodies are skipped
25
+ */
26
+ export declare const INLINES: ReadonlySet<string>;
27
+ /**
28
+ * The direction an annotation gives a parameter, if it is one: the ones of Microsoft (_Out_, _Inout_opt_,
29
+ * _Outptr_result_maybenull_…) and the macros IN and OUT of the Windows API; null for an annotation that gives none,
30
+ * undefined for a word that is not an annotation
31
+ * @param word
32
+ */
33
+ export declare function annotationDirection(word: string): Direction | null | undefined;
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INLINES = exports.CONSTS = exports.IGNORED = exports.CONVENTIONS = void 0;
4
+ exports.builtinTypes = builtinTypes;
5
+ exports.annotationDirection = annotationDirection;
6
+ const primitive = (name) => ({ kind: 'primitive', name: name });
7
+ const pointer = (to, constTarget = false) => ({ kind: 'pointer', to: to, constTarget: constTarget });
8
+ /**
9
+ * The opaque FILE of the C library, the same object for every library: the pointers of two libraries are the same type
10
+ */
11
+ const FILE = { kind: 'struct', tag: 'FILE' };
12
+ /**
13
+ * The va_list of the variadic functions: declared, it cannot be passed (the functions taking one are for C callers)
14
+ */
15
+ const VA_LIST = { kind: 'struct', tag: 'va_list' };
16
+ /**
17
+ * The GUID of Windows (UUID, CLSID, IID)
18
+ */
19
+ const GUID = {
20
+ kind: 'struct',
21
+ tag: '_GUID',
22
+ members: [
23
+ { name: 'Data1', type: primitive('uint32') },
24
+ { name: 'Data2', type: primitive('uint16') },
25
+ { name: 'Data3', type: primitive('uint16') },
26
+ { name: 'Data4', type: { kind: 'array', of: primitive('uint8'), length: 8 } }
27
+ ]
28
+ };
29
+ /**
30
+ * The handles of Windows: pointers to nothing known
31
+ */
32
+ const HANDLES = [
33
+ 'HANDLE', 'HWND', 'HINSTANCE', 'HMODULE', 'HKEY', 'HDC', 'HGLOBAL', 'HLOCAL', 'HMENU', 'HICON', 'HBITMAP', 'HBRUSH',
34
+ 'HFONT', 'HPEN', 'HRGN', 'HCURSOR', 'HMONITOR', 'HRSRC', 'HGDIOBJ', 'HACCEL', 'HDESK', 'HWINSTA', 'HHOOK', 'HEVENT',
35
+ 'HKL', 'HPALETTE', 'HDROP', 'HDEVINFO', 'SC_HANDLE', 'SERVICE_STATUS_HANDLE', 'PSID', 'HCRYPTPROV'
36
+ ];
37
+ /**
38
+ * The types every declaration knows without declaring them: the ones of stdint.h and stddef.h, the ones of POSIX with
39
+ * their size on the platform, the ones of the Windows API (the same on every platform: DWORD is 32-bit everywhere).
40
+ * A declaration of the same name replaces them
41
+ * @param platform
42
+ */
43
+ function builtinTypes(platform) {
44
+ const windows = platform.os === 'win32';
45
+ const darwin = platform.os === 'darwin';
46
+ const types = new Map();
47
+ const set = (names, type) => {
48
+ for (const name of typeof names === 'string' ? [names] : names)
49
+ types.set(name, type);
50
+ };
51
+ //stdint.h, stddef.h, uchar.h, stdbool.h
52
+ for (const bits of [8, 16, 32, 64]) {
53
+ set([`int${bits}_t`, `int_least${bits}_t`, `int_fast${bits}_t`, `__int${bits}_t`], primitive(`int${bits}`));
54
+ set([`uint${bits}_t`, `uint_least${bits}_t`, `uint_fast${bits}_t`, `__uint${bits}_t`, `u_int${bits}_t`], primitive(`uint${bits}`));
55
+ }
56
+ set(['intmax_t'], primitive('int64'));
57
+ set(['uintmax_t'], primitive('uint64'));
58
+ set(['size_t', 'rsize_t'], primitive('size'));
59
+ set(['ssize_t'], primitive('ssize'));
60
+ set(['intptr_t'], primitive('intptr'));
61
+ set(['uintptr_t'], primitive('uintptr'));
62
+ set(['ptrdiff_t'], primitive('ptrdiff'));
63
+ set(['wchar_t'], primitive('wchar'));
64
+ set(['char16_t'], primitive('char16'));
65
+ set(['char32_t'], primitive('char32'));
66
+ set(['bool'], primitive('bool'));
67
+ //POSIX, with their size on the platform
68
+ set(['time_t'], primitive(windows ? 'int64' : 'long'));
69
+ set(['suseconds_t'], primitive(darwin ? 'int32' : 'long'));
70
+ set(['useconds_t'], primitive('uint32'));
71
+ set(['off_t'], primitive(darwin ? 'int64' : 'long'));
72
+ set(['clock_t'], primitive(darwin ? 'ulong' : 'long'));
73
+ set(['pid_t'], primitive('int32'));
74
+ set(['uid_t', 'gid_t', 'socklen_t'], primitive('uint32'));
75
+ set(['mode_t'], primitive(darwin ? 'uint16' : 'uint32'));
76
+ set(['FILE'], FILE);
77
+ set(['va_list', '__builtin_va_list', '__gnuc_va_list', '__darwin_va_list', '__va_list'], VA_LIST);
78
+ //The BSD types (sys/types.h)
79
+ set(['u_char'], primitive('uint8'));
80
+ set(['u_short'], primitive('uint16'));
81
+ set(['u_int'], primitive('uint32'));
82
+ set(['u_long'], primitive('ulong'));
83
+ set(['u_quad_t'], primitive('uint64'));
84
+ set(['quad_t'], primitive('int64'));
85
+ set(['caddr_t'], pointer(primitive('char')));
86
+ //The Windows API
87
+ set(['BYTE', 'UCHAR', 'BOOLEAN', 'UINT8'], primitive('uint8'));
88
+ set(['CHAR', 'CCHAR'], primitive('char'));
89
+ set(['INT8'], primitive('int8'));
90
+ set(['WCHAR'], primitive('char16'));
91
+ set(['SHORT', 'INT16'], primitive('int16'));
92
+ set(['USHORT', 'WORD', 'ATOM', 'LANGID', 'UINT16'], primitive('uint16'));
93
+ set(['INT', 'LONG', 'BOOL', 'HRESULT', 'NTSTATUS', 'LONG32', 'INT32', 'HFILE'], primitive('int32'));
94
+ set(['UINT', 'ULONG', 'DWORD', 'DWORD32', 'ULONG32', 'UINT32', 'COLORREF', 'LCID'], primitive('uint32'));
95
+ set(['LONGLONG', 'LONG64', 'INT64'], primitive('int64'));
96
+ set(['ULONGLONG', 'DWORDLONG', 'QWORD', 'DWORD64', 'ULONG64', 'UINT64'], primitive('uint64'));
97
+ set(['FLOAT'], primitive('float'));
98
+ set(['DOUBLE'], primitive('double'));
99
+ set(['INT_PTR', 'LONG_PTR', 'LPARAM', 'LRESULT', 'SSIZE_T'], primitive('intptr'));
100
+ set(['UINT_PTR', 'ULONG_PTR', 'DWORD_PTR', 'WPARAM', 'SIZE_T'], primitive('uintptr'));
101
+ set(HANDLES, pointer(primitive('void')));
102
+ set(['PVOID', 'LPVOID'], pointer(primitive('void')));
103
+ set(['LPCVOID', 'PCVOID'], pointer(primitive('void'), true));
104
+ set(['LPSTR', 'PSTR', 'PCHAR'], pointer(primitive('char')));
105
+ set(['LPCSTR', 'PCSTR'], pointer(primitive('char'), true));
106
+ set(['LPWSTR', 'PWSTR', 'PWCHAR'], pointer(primitive('char16')));
107
+ set(['LPCWSTR', 'PCWSTR'], pointer(primitive('char16'), true));
108
+ set(['LPBYTE', 'PBYTE', 'PUCHAR', 'PBOOLEAN'], pointer(primitive('uint8')));
109
+ set(['LPWORD', 'PWORD', 'PUSHORT'], pointer(primitive('uint16')));
110
+ set(['LPDWORD', 'PDWORD', 'PULONG', 'PUINT', 'LPUINT'], pointer(primitive('uint32')));
111
+ set(['LPLONG', 'PLONG', 'LPINT', 'PINT', 'LPBOOL', 'PBOOL'], pointer(primitive('int32')));
112
+ set(['PULONGLONG', 'PDWORD64'], pointer(primitive('uint64')));
113
+ set(['PLONGLONG'], pointer(primitive('int64')));
114
+ set(['PSIZE_T', 'PULONG_PTR', 'PDWORD_PTR'], pointer(primitive('uintptr')));
115
+ set(['PHANDLE', 'LPHANDLE'], pointer(pointer(primitive('void'))));
116
+ set(['GUID', 'UUID', 'CLSID', 'IID'], GUID);
117
+ return types;
118
+ }
119
+ /**
120
+ * The words naming a calling convention: the keywords of the compilers, the macros of the Windows API
121
+ */
122
+ exports.CONVENTIONS = new Map([
123
+ ['__cdecl', 'cdecl'], ['_cdecl', 'cdecl'], ['__stdcall', 'stdcall'], ['_stdcall', 'stdcall'],
124
+ ['__fastcall', 'fastcall'], ['_fastcall', 'fastcall'], ['__thiscall', 'thiscall'],
125
+ ['WINAPI', 'stdcall'], ['APIENTRY', 'stdcall'], ['CALLBACK', 'stdcall'], ['PASCAL', 'stdcall'], ['NTAPI', 'stdcall'],
126
+ ['STDMETHODCALLTYPE', 'stdcall'], ['STDAPICALLTYPE', 'stdcall'], ['WINAPIV', 'cdecl'], ['CDECL', 'cdecl']
127
+ ]);
128
+ /**
129
+ * The words read and ignored where qualifiers go: the qualifiers that do not change how a value is passed, the macros of
130
+ * the Windows API that are empty
131
+ */
132
+ exports.IGNORED = new Set([
133
+ 'volatile', 'restrict', '__restrict', '__restrict__', '_Nullable', '_Nonnull', '_Null_unspecified', '__nullable',
134
+ '__nonnull', '__null_unspecified', '_Nullable_result', '__unaligned', '__ptr32', '__ptr64', '__w64', '__sptr', '__uptr',
135
+ 'FAR', 'NEAR', 'far', 'near', 'OPTIONAL', 'register', 'auto', '__extension__', '_Noreturn', 'noreturn', '__volatile__',
136
+ '__kindof', '__cdecl__', '__weak', '__strong', '__autoreleasing', '__unsafe_unretained'
137
+ ]);
138
+ /**
139
+ * The words read as const
140
+ */
141
+ exports.CONSTS = new Set(['const', '__const', '__const__', 'CONST']);
142
+ /**
143
+ * The words of inline functions, whose bodies are skipped
144
+ */
145
+ exports.INLINES = new Set(['inline', '__inline', '__inline__', '__forceinline', '_inline', 'FORCEINLINE']);
146
+ /**
147
+ * The prefixes of the annotations of Microsoft (SAL): a word starting with one, not declared as a type, is an
148
+ * annotation, its arguments in parentheses skipped
149
+ */
150
+ const SAL_PREFIXES = [
151
+ '_In_', '_Out', '_Inout', '_COM_Outptr', '_Ret_', '_Success_', '_Check_return_', '_Must_inspect_result_',
152
+ '_Printf_format_string_', '_Scanf_format_string_', '_Field_', '_Frees_ptr_', '_Pre_', '_Post_', '_Deref_', '_Reserved_',
153
+ '_Null_terminated_', '_NullNull_terminated_', '_Use_decl_annotations_', '_When_', '_At_', '_At_buffer_', '_Always_',
154
+ '_Return_type_success_', '_Struct_size_bytes_', '_Readable_', '_Writable_', '__drv_', '_IRQL_', '_Kernel_',
155
+ '_Function_class_', '_Callback_', '_Notnull_', '_Maybenull_', '_Const_', '_Translates_', '_Points_to_data_',
156
+ '_Literal_', '_Notliteral_', '_Strict_type_match_', '_Analysis_', '_Acquires_', '_Releases_', '_Requires_', '_Guarded_by_',
157
+ '_Interlocked_', '_Inexpressible_', '_Pre_satisfies_', '_Outref_', '_Ret_maybenull_', '_Result_', '_Group_', '_On_failure_'
158
+ ];
159
+ /**
160
+ * The direction an annotation gives a parameter, if it is one: the ones of Microsoft (_Out_, _Inout_opt_,
161
+ * _Outptr_result_maybenull_…) and the macros IN and OUT of the Windows API; null for an annotation that gives none,
162
+ * undefined for a word that is not an annotation
163
+ * @param word
164
+ */
165
+ function annotationDirection(word) {
166
+ if (word === 'IN')
167
+ return 'in';
168
+ if (word === 'OUT')
169
+ return 'out';
170
+ if (!SAL_PREFIXES.some((prefix) => word.startsWith(prefix)) && !['_In_', '_Out_', '_Inout_'].includes(word))
171
+ return undefined;
172
+ if (word.startsWith('_Inout'))
173
+ return 'inout';
174
+ if (word.startsWith('_Out') || word.startsWith('_COM_Outptr'))
175
+ return 'out';
176
+ if (word.startsWith('_In_'))
177
+ return 'in';
178
+ return null;
179
+ }