@lakutata/ffi 0.0.0-stage → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +163 -2
- package/README.zh-CN.md +140 -0
- package/dist/cjs/components/NativeLibrary.d.ts +229 -0
- package/dist/cjs/components/NativeLibrary.js +346 -0
- package/dist/cjs/decorators/Library.d.ts +61 -0
- package/dist/cjs/decorators/Library.js +44 -0
- package/dist/cjs/decorators/Native.d.ts +69 -0
- package/dist/cjs/decorators/Native.js +90 -0
- package/dist/cjs/exceptions/NativeDeclarationException.d.ts +12 -0
- package/dist/cjs/exceptions/NativeDeclarationException.js +19 -0
- package/dist/cjs/exceptions/NativeLibraryClosedException.d.ts +12 -0
- package/dist/cjs/exceptions/NativeLibraryClosedException.js +19 -0
- package/dist/cjs/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
- package/dist/cjs/exceptions/NativeLibraryNotFoundException.js +18 -0
- package/dist/cjs/exceptions/NativeMemoryException.d.ts +11 -0
- package/dist/cjs/exceptions/NativeMemoryException.js +18 -0
- package/dist/cjs/exports/FFI.d.ts +12 -0
- package/dist/cjs/exports/FFI.js +40 -0
- package/dist/cjs/exports/Generator.d.ts +2 -0
- package/dist/cjs/exports/Generator.js +10 -0
- package/dist/cjs/lib/Memory.d.ts +188 -0
- package/dist/cjs/lib/Memory.js +227 -0
- package/dist/cjs/lib/Runtime.d.ts +98 -0
- package/dist/cjs/lib/Runtime.js +209 -0
- package/dist/cjs/lib/backend/Functions.d.ts +65 -0
- package/dist/cjs/lib/backend/Functions.js +214 -0
- package/dist/cjs/lib/backend/KoffiTypes.d.ts +53 -0
- package/dist/cjs/lib/backend/KoffiTypes.js +426 -0
- package/dist/cjs/lib/declarations/Builtins.d.ts +33 -0
- package/dist/cjs/lib/declarations/Builtins.js +179 -0
- package/dist/cjs/lib/declarations/Layout.d.ts +41 -0
- package/dist/cjs/lib/declarations/Layout.js +180 -0
- package/dist/cjs/lib/declarations/Lexer.d.ts +40 -0
- package/dist/cjs/lib/declarations/Lexer.js +325 -0
- package/dist/cjs/lib/declarations/Parser.d.ts +90 -0
- package/dist/cjs/lib/declarations/Parser.js +1425 -0
- package/dist/cjs/lib/declarations/Platform.d.ts +50 -0
- package/dist/cjs/lib/declarations/Platform.js +27 -0
- package/dist/cjs/lib/declarations/Types.d.ts +173 -0
- package/dist/cjs/lib/declarations/Types.js +179 -0
- package/dist/cjs/lib/generator/CText.d.ts +14 -0
- package/dist/cjs/lib/generator/CText.js +133 -0
- package/dist/cjs/lib/generator/Generator.d.ts +136 -0
- package/dist/cjs/lib/generator/Generator.js +395 -0
- package/dist/cjs/lib/generator/Preprocessor.d.ts +60 -0
- package/dist/cjs/lib/generator/Preprocessor.js +94 -0
- package/dist/cjs/lib/generator/TypeScriptTypes.d.ts +35 -0
- package/dist/cjs/lib/generator/TypeScriptTypes.js +153 -0
- package/dist/cjs/lib/library/NativeCallback.d.ts +44 -0
- package/dist/cjs/lib/library/NativeCallback.js +66 -0
- package/dist/cjs/lib/library/NativeLibraryHandle.d.ts +268 -0
- package/dist/cjs/lib/library/NativeLibraryHandle.js +540 -0
- package/dist/cjs/lib/library/Resolve.d.ts +49 -0
- package/dist/cjs/lib/library/Resolve.js +130 -0
- package/dist/cjs/lib/thread/NativeThreads.d.ts +80 -0
- package/dist/cjs/lib/thread/NativeThreads.js +384 -0
- package/dist/cjs/lib/thread/NativeWorker.d.ts +6 -0
- package/dist/cjs/lib/thread/NativeWorker.js +96 -0
- package/dist/cjs/lib/thread/NativeWorkerCode.d.ts +5 -0
- package/dist/cjs/lib/thread/NativeWorkerCode.js +4 -0
- package/dist/cjs/lib/thread/PackageRequire.d.ts +6 -0
- package/dist/cjs/lib/thread/PackageRequire.js +4 -0
- package/dist/cjs/lib/thread/ThreadProtocol.d.ts +77 -0
- package/dist/cjs/lib/thread/ThreadProtocol.js +2 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/components/NativeLibrary.js +338 -0
- package/dist/esm/decorators/Library.js +40 -0
- package/dist/esm/decorators/Native.js +85 -0
- package/dist/esm/exceptions/NativeDeclarationException.js +15 -0
- package/dist/esm/exceptions/NativeLibraryClosedException.js +15 -0
- package/dist/esm/exceptions/NativeLibraryNotFoundException.js +14 -0
- package/dist/esm/exceptions/NativeMemoryException.js +14 -0
- package/dist/esm/exports/FFI.js +10 -0
- package/dist/esm/exports/Generator.js +2 -0
- package/dist/esm/lib/Memory.js +210 -0
- package/dist/esm/lib/Runtime.js +192 -0
- package/dist/esm/lib/backend/Functions.js +206 -0
- package/dist/esm/lib/backend/KoffiTypes.js +418 -0
- package/dist/esm/lib/declarations/Builtins.js +174 -0
- package/dist/esm/lib/declarations/Layout.js +175 -0
- package/dist/esm/lib/declarations/Lexer.js +321 -0
- package/dist/esm/lib/declarations/Parser.js +1421 -0
- package/dist/esm/lib/declarations/Platform.js +23 -0
- package/dist/esm/lib/declarations/Types.js +170 -0
- package/dist/esm/lib/generator/CText.js +129 -0
- package/dist/esm/lib/generator/Generator.js +388 -0
- package/dist/esm/lib/generator/Preprocessor.js +87 -0
- package/dist/esm/lib/generator/TypeScriptTypes.js +149 -0
- package/dist/esm/lib/library/NativeCallback.js +59 -0
- package/dist/esm/lib/library/NativeLibraryHandle.js +532 -0
- package/dist/esm/lib/library/Resolve.js +122 -0
- package/dist/esm/lib/thread/NativeThreads.js +377 -0
- package/dist/esm/lib/thread/NativeWorker.js +93 -0
- package/dist/esm/lib/thread/NativeWorkerCode.js +1 -0
- package/dist/esm/lib/thread/PackageRequire.js +2 -0
- package/dist/esm/lib/thread/ThreadProtocol.js +1 -0
- package/dist/types/components/NativeLibrary.d.ts +229 -0
- package/dist/types/decorators/Library.d.ts +61 -0
- package/dist/types/decorators/Native.d.ts +69 -0
- package/dist/types/exceptions/NativeDeclarationException.d.ts +12 -0
- package/dist/types/exceptions/NativeLibraryClosedException.d.ts +12 -0
- package/dist/types/exceptions/NativeLibraryNotFoundException.d.ts +11 -0
- package/dist/types/exceptions/NativeMemoryException.d.ts +11 -0
- package/dist/types/exports/FFI.d.ts +12 -0
- package/dist/types/exports/Generator.d.ts +2 -0
- package/dist/types/lib/Memory.d.ts +188 -0
- package/dist/types/lib/Runtime.d.ts +98 -0
- package/dist/types/lib/backend/Functions.d.ts +65 -0
- package/dist/types/lib/backend/KoffiTypes.d.ts +53 -0
- package/dist/types/lib/declarations/Builtins.d.ts +33 -0
- package/dist/types/lib/declarations/Layout.d.ts +41 -0
- package/dist/types/lib/declarations/Lexer.d.ts +40 -0
- package/dist/types/lib/declarations/Parser.d.ts +90 -0
- package/dist/types/lib/declarations/Platform.d.ts +50 -0
- package/dist/types/lib/declarations/Types.d.ts +173 -0
- package/dist/types/lib/generator/CText.d.ts +14 -0
- package/dist/types/lib/generator/Generator.d.ts +136 -0
- package/dist/types/lib/generator/Preprocessor.d.ts +60 -0
- package/dist/types/lib/generator/TypeScriptTypes.d.ts +35 -0
- package/dist/types/lib/library/NativeCallback.d.ts +44 -0
- package/dist/types/lib/library/NativeLibraryHandle.d.ts +268 -0
- package/dist/types/lib/library/Resolve.d.ts +49 -0
- package/dist/types/lib/thread/NativeThreads.d.ts +80 -0
- package/dist/types/lib/thread/NativeWorker.d.ts +6 -0
- package/dist/types/lib/thread/NativeWorkerCode.d.ts +5 -0
- package/dist/types/lib/thread/PackageRequire.d.ts +6 -0
- package/dist/types/lib/thread/ThreadProtocol.d.ts +77 -0
- package/package.json +85 -4
|
@@ -0,0 +1,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
|
+
}
|