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