@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,41 @@
1
+ import type { Platform } from './Platform.js';
2
+ import { type CType, type Member, type RecordType } from './Types.js';
3
+ /**
4
+ * Where a member of a struct is: its offset in bytes; for a bitfield, its first bit (from the start of the struct, the
5
+ * lowest bits first) and its width
6
+ */
7
+ export interface MemberLayout {
8
+ readonly member: Member;
9
+ readonly offset: number;
10
+ readonly size: number;
11
+ readonly bitOffset?: number;
12
+ readonly bits?: number;
13
+ }
14
+ export interface RecordLayout {
15
+ readonly size: number;
16
+ readonly alignment: number;
17
+ readonly members: readonly MemberLayout[];
18
+ /**
19
+ * Whether it has bitfields (koffi does not lay them out: the package does)
20
+ */
21
+ readonly bitfields: boolean;
22
+ }
23
+ /**
24
+ * The size of a type, in bytes
25
+ * @param type
26
+ * @param platform
27
+ */
28
+ export declare function sizeOf(type: CType, platform: Platform): number;
29
+ /**
30
+ * The alignment of a type, in bytes
31
+ * @param type
32
+ * @param platform
33
+ */
34
+ export declare function alignmentOf(type: CType, platform: Platform): number;
35
+ /**
36
+ * The layout of a struct or of a union, as the compilers of the platform lay it out (GCC and Clang, or Microsoft's for
37
+ * the bitfields): the alignment of each member capped by #pragma pack, raised by its aligned attribute
38
+ * @param record
39
+ * @param platform
40
+ */
41
+ export declare function layoutOf(record: RecordType, platform: Platform): RecordLayout;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sizeOf = sizeOf;
4
+ exports.alignmentOf = alignmentOf;
5
+ exports.layoutOf = layoutOf;
6
+ const NativeDeclarationException_js_1 = require("../../exceptions/NativeDeclarationException.js");
7
+ const Types_js_1 = require("./Types.js");
8
+ const layouts = new WeakMap();
9
+ /**
10
+ * The size of a type, in bytes
11
+ * @param type
12
+ * @param platform
13
+ */
14
+ function sizeOf(type, platform) {
15
+ switch (type.kind) {
16
+ case 'primitive':
17
+ return (0, Types_js_1.primitiveSize)(type.name, platform);
18
+ case 'pointer':
19
+ return platform.pointerSize;
20
+ case 'array':
21
+ return sizeOf(type.of, platform) * type.length;
22
+ case 'enum':
23
+ return (0, Types_js_1.primitiveSize)(type.base, platform);
24
+ case 'struct':
25
+ case 'union':
26
+ return layoutOf(type, platform).size;
27
+ case 'function':
28
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`A function has no size: ${(0, Types_js_1.typeText)(type)}`);
29
+ }
30
+ }
31
+ /**
32
+ * The alignment of a type, in bytes
33
+ * @param type
34
+ * @param platform
35
+ */
36
+ function alignmentOf(type, platform) {
37
+ switch (type.kind) {
38
+ case 'primitive':
39
+ case 'enum': {
40
+ const size = (0, Types_js_1.primitiveSize)(type.kind === 'primitive' ? type.name : type.base, platform);
41
+ return size === 8 ? platform.int64Alignment : Math.max(size, 1);
42
+ }
43
+ case 'pointer':
44
+ return platform.pointerSize;
45
+ case 'array':
46
+ return alignmentOf(type.of, platform);
47
+ case 'struct':
48
+ case 'union':
49
+ return layoutOf(type, platform).alignment;
50
+ case 'function':
51
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`A function has no alignment: ${(0, Types_js_1.typeText)(type)}`);
52
+ }
53
+ }
54
+ /**
55
+ * Round up to a multiple
56
+ * @param value
57
+ * @param multiple
58
+ */
59
+ function alignUp(value, multiple) {
60
+ return Math.ceil(value / multiple) * multiple;
61
+ }
62
+ /**
63
+ * The layout of a struct or of a union, as the compilers of the platform lay it out (GCC and Clang, or Microsoft's for
64
+ * the bitfields): the alignment of each member capped by #pragma pack, raised by its aligned attribute
65
+ * @param record
66
+ * @param platform
67
+ */
68
+ function layoutOf(record, platform) {
69
+ let cached = layouts.get(record);
70
+ const found = cached?.get(platform);
71
+ if (found)
72
+ return found;
73
+ if (!record.members)
74
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The ${record.kind} ${record.tag ?? '(anonymous)'} is declared but not defined: it is used through pointers only`);
75
+ const layout = record.kind === 'union' ? unionLayout(record, platform) : structLayout(record, platform);
76
+ if (!cached)
77
+ layouts.set(record, cached = new Map());
78
+ cached.set(platform, layout);
79
+ return layout;
80
+ }
81
+ /**
82
+ * The alignment of a member: its type's, raised by its aligned attribute, capped by the packing of its struct
83
+ * @param member
84
+ * @param record
85
+ * @param platform
86
+ */
87
+ function memberAlignment(member, record, platform) {
88
+ const natural = Math.max(alignmentOf(member.type, platform), member.align ?? 1);
89
+ return record.pack ? Math.min(natural, record.pack) : natural;
90
+ }
91
+ /**
92
+ * The layout of a union: every member at 0
93
+ * @param record
94
+ * @param platform
95
+ */
96
+ function unionLayout(record, platform) {
97
+ let size = 0;
98
+ let alignment = record.align ?? 1;
99
+ const members = [];
100
+ for (const member of record.members) {
101
+ if (member.bits !== undefined)
102
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} of a union is not supported`);
103
+ const memberSize = sizeOf(member.type, platform);
104
+ alignment = Math.max(alignment, memberAlignment(member, record, platform));
105
+ size = Math.max(size, memberSize);
106
+ members.push({ member: member, offset: 0, size: memberSize });
107
+ }
108
+ return { size: alignUp(size, alignment), alignment: alignment, members: members, bitfields: false };
109
+ }
110
+ /**
111
+ * The layout of a struct: the members one after the other, each at its alignment; the bitfields in the storage units of
112
+ * their types, by the rules of the platform
113
+ * @param record
114
+ * @param platform
115
+ */
116
+ function structLayout(record, platform) {
117
+ let bit = 0;
118
+ let alignment = record.align ?? 1;
119
+ let bitfields = false;
120
+ //The storage unit of the bitfields being filled (Microsoft's rules): its offset in bytes, its size, the bits used
121
+ let unit;
122
+ const members = [];
123
+ for (const member of record.members) {
124
+ if (member.bits === undefined) {
125
+ unit = undefined;
126
+ const aligned = memberAlignment(member, record, platform);
127
+ const offset = alignUp(Math.ceil(bit / 8), aligned);
128
+ const size = sizeOf(member.type, platform);
129
+ members.push({ member: member, offset: offset, size: size });
130
+ bit = (offset + size) * 8;
131
+ alignment = Math.max(alignment, aligned);
132
+ continue;
133
+ }
134
+ bitfields = true;
135
+ if (record.pack !== undefined)
136
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} of a packed struct is not supported`);
137
+ if (member.type.kind !== 'primitive' && member.type.kind !== 'enum')
138
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} must have an integer type`);
139
+ const size = sizeOf(member.type, platform);
140
+ const typeAlignment = alignmentOf(member.type, platform);
141
+ const width = member.bits;
142
+ if (width > size * 8)
143
+ throw new NativeDeclarationException_js_1.NativeDeclarationException(`The bitfield ${member.name || '(unnamed)'} is wider than its type (${width} bits)`);
144
+ if (platform.bitfields === 'msvc') {
145
+ if (width === 0) {
146
+ //After a bitfield: ends its storage unit, the next member at the alignment of its type; ignored otherwise
147
+ if (unit) {
148
+ bit = alignUp((unit.offset + unit.size) * 8, typeAlignment * 8);
149
+ alignment = Math.max(alignment, typeAlignment);
150
+ }
151
+ unit = undefined;
152
+ continue;
153
+ }
154
+ if (!unit || unit.size !== size || unit.used + width > size * 8) {
155
+ const start = unit ? unit.offset + unit.size : Math.ceil(bit / 8);
156
+ unit = { offset: alignUp(start, typeAlignment), size: size, used: 0 };
157
+ alignment = Math.max(alignment, typeAlignment);
158
+ }
159
+ members.push({ member: member, offset: unit.offset, size: size, bitOffset: unit.offset * 8 + unit.used, bits: width });
160
+ unit.used += width;
161
+ bit = (unit.offset + unit.size) * 8;
162
+ continue;
163
+ }
164
+ //GCC and Clang: in the first unit of the size of its type where it fits, from the current bit. The type of an
165
+ //unnamed bitfield aligns the struct by the rules of ARM only
166
+ if (member.name || platform.bitfields === 'aapcs')
167
+ alignment = Math.max(alignment, typeAlignment);
168
+ if (width === 0) {
169
+ bit = alignUp(bit, typeAlignment * 8);
170
+ continue;
171
+ }
172
+ if ((bit % (size * 8)) + width > size * 8)
173
+ bit = alignUp(bit, typeAlignment * 8);
174
+ members.push({ member: member, offset: Math.floor(bit / (size * 8)) * size, size: size, bitOffset: bit, bits: width });
175
+ bit += width;
176
+ }
177
+ if (unit)
178
+ bit = (unit.offset + unit.size) * 8;
179
+ return { size: alignUp(Math.ceil(bit / 8), alignment), alignment: alignment, members: members, bitfields: bitfields };
180
+ }
@@ -0,0 +1,40 @@
1
+ import { NativeDeclarationException } from '../../exceptions/NativeDeclarationException.js';
2
+ export type TokenKind = 'identifier' | 'number' | 'char' | 'string' | 'punctuator' | 'pragma' | 'define' | 'undef' | 'end';
3
+ export interface Token {
4
+ readonly kind: TokenKind;
5
+ readonly text: string;
6
+ /**
7
+ * The value of an integer or of a character
8
+ */
9
+ readonly value?: bigint;
10
+ /**
11
+ * Whether a number is a floating-point one
12
+ */
13
+ readonly float?: boolean;
14
+ readonly line: number;
15
+ readonly column: number;
16
+ /**
17
+ * In a preprocessed text, the file it comes from and its line there (from the line markers)
18
+ */
19
+ readonly file?: string;
20
+ readonly fileLine?: number;
21
+ /**
22
+ * The text of a string as written, its quotes and escapes included
23
+ */
24
+ readonly source?: string;
25
+ }
26
+ /**
27
+ * The exception of a declaration that cannot be read, at a line and a column
28
+ * @param message
29
+ * @param line
30
+ * @param column
31
+ */
32
+ export declare function declarationError(message: string, line: number, column: number): NativeDeclarationException;
33
+ /**
34
+ * The tokens of C declarations. Comments are skipped; of the directives of the preprocessor, #pragma pack, #define and
35
+ * #undef are kept (as tokens holding the rest of their line), #include and the other #pragma are skipped, and the
36
+ * conditional ones (#if, #ifdef…) are refused: what they select is the work of a preprocessor. The line markers of a
37
+ * preprocessed text give each token the file and the line it comes from
38
+ * @param text
39
+ */
40
+ export declare function tokenize(text: string): Token[];
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.declarationError = declarationError;
4
+ exports.tokenize = tokenize;
5
+ const NativeDeclarationException_js_1 = require("../../exceptions/NativeDeclarationException.js");
6
+ const PUNCTUATORS = ['...', '<<=', '>>=', '<<', '>>', '<=', '>=', '==', '!=', '&&', '||', '->', '++', '--', '##', '::'];
7
+ const SINGLE = '{}()[];,*:=+-/%&|^~!?<>.#';
8
+ const ESCAPES = { n: 10, t: 9, r: 13, '0': 0, a: 7, b: 8, f: 12, v: 11, '\\': 92, '\'': 39, '"': 34, '?': 63, e: 27 };
9
+ /**
10
+ * The directives of the preprocessor that change what is declared: the declarations given must be preprocessed
11
+ */
12
+ const CONDITIONALS = ['if', 'ifdef', 'ifndef', 'elif', 'elifdef', 'elifndef', 'else', 'endif'];
13
+ /**
14
+ * The end of a directive of the preprocessor: the end of its line, its continued lines and the block comments that go on
15
+ * past it included (not the ones inside its strings)
16
+ * @param text
17
+ * @param start
18
+ */
19
+ function directiveEnd(text, start) {
20
+ let end = start;
21
+ while (end < text.length && text[end] !== '\n') {
22
+ const char = text[end];
23
+ if (char === '\\' && text[end + 1] === '\n') {
24
+ end += 2;
25
+ }
26
+ else if (char === '\\' && text[end + 1] === '\r' && text[end + 2] === '\n') {
27
+ end += 3;
28
+ }
29
+ else if (char === '/' && text[end + 1] === '*') {
30
+ const close = text.indexOf('*/', end + 2);
31
+ end = close < 0 ? text.length : close + 2;
32
+ }
33
+ else if (char === '/' && text[end + 1] === '/') {
34
+ while (end < text.length && text[end] !== '\n')
35
+ end++;
36
+ }
37
+ else if (char === '"' || char === '\'') {
38
+ end++;
39
+ while (end < text.length && text[end] !== char && text[end] !== '\n')
40
+ end += text[end] === '\\' ? 2 : 1;
41
+ if (text[end] === char)
42
+ end++;
43
+ }
44
+ else {
45
+ end++;
46
+ }
47
+ }
48
+ return end;
49
+ }
50
+ /**
51
+ * The text of a directive without its comments (the ones inside its strings and characters kept), its continued lines
52
+ * joined
53
+ * @param text
54
+ */
55
+ function withoutComments(text) {
56
+ let result = '';
57
+ let i = 0;
58
+ while (i < text.length) {
59
+ const char = text[i];
60
+ if (char === '/' && text[i + 1] === '*') {
61
+ const close = text.indexOf('*/', i + 2);
62
+ i = close < 0 ? text.length : close + 2;
63
+ result += ' ';
64
+ }
65
+ else if (char === '/' && text[i + 1] === '/') {
66
+ while (i < text.length && text[i] !== '\n')
67
+ i++;
68
+ }
69
+ else if (char === '"' || char === '\'') {
70
+ let end = i + 1;
71
+ while (end < text.length && text[end] !== char && text[end] !== '\n')
72
+ end += text[end] === '\\' ? 2 : 1;
73
+ result += text.slice(i, Math.min(end + 1, text.length));
74
+ i = end + 1;
75
+ }
76
+ else if (char === '\\' && (text[i + 1] === '\n' || (text[i + 1] === '\r' && text[i + 2] === '\n'))) {
77
+ i += text[i + 1] === '\n' ? 2 : 3;
78
+ result += ' ';
79
+ }
80
+ else {
81
+ result += char;
82
+ i++;
83
+ }
84
+ }
85
+ return result.trim();
86
+ }
87
+ /**
88
+ * The offsets the lines of a text start at
89
+ * @param text
90
+ */
91
+ function lineStartsOf(text) {
92
+ const starts = [0];
93
+ for (let i = 0; i < text.length; i++)
94
+ if (text.charCodeAt(i) === 10)
95
+ starts.push(i + 1);
96
+ return starts;
97
+ }
98
+ /**
99
+ * Where an offset of a text is, for the messages: its line and its column (from 1)
100
+ * @param starts the offsets the lines start at
101
+ * @param offset
102
+ */
103
+ function positionOf(starts, offset) {
104
+ let low = 0;
105
+ let high = starts.length - 1;
106
+ while (low < high) {
107
+ const middle = (low + high + 1) >> 1;
108
+ if (starts[middle] <= offset)
109
+ low = middle;
110
+ else
111
+ high = middle - 1;
112
+ }
113
+ return { line: low + 1, column: offset - starts[low] + 1 };
114
+ }
115
+ /**
116
+ * The exception of a declaration that cannot be read, at a line and a column
117
+ * @param message
118
+ * @param line
119
+ * @param column
120
+ */
121
+ function declarationError(message, line, column) {
122
+ return new NativeDeclarationException_js_1.NativeDeclarationException(`${message} (line ${line}, column ${column})`);
123
+ }
124
+ /**
125
+ * The file and the line a line marker of a preprocessor gives to the line after it: `# 12 "file.h" 2` (GCC, Clang),
126
+ * `#line 12 "file.h"` (Microsoft); undefined for another directive
127
+ * @param directive the directive, without its #
128
+ */
129
+ function lineMarker(directive) {
130
+ const marker = directive.match(/^(?:line\s+)?(\d+)(?:\s+"((?:[^"\\]|\\.)*)")?/);
131
+ if (!marker)
132
+ return undefined;
133
+ return { line: Number(marker[1]), ...marker[2] !== undefined ? { file: marker[2].replace(/\\(.)/g, '$1') } : {} };
134
+ }
135
+ /**
136
+ * The tokens of C declarations. Comments are skipped; of the directives of the preprocessor, #pragma pack, #define and
137
+ * #undef are kept (as tokens holding the rest of their line), #include and the other #pragma are skipped, and the
138
+ * conditional ones (#if, #ifdef…) are refused: what they select is the work of a preprocessor. The line markers of a
139
+ * preprocessed text give each token the file and the line it comes from
140
+ * @param text
141
+ */
142
+ function tokenize(text) {
143
+ const tokens = [];
144
+ const length = text.length;
145
+ let i = 0;
146
+ let lineStart = true;
147
+ const starts = lineStartsOf(text);
148
+ //The file of the line markers, and what to add to a line of the text to get its line in that file
149
+ let file;
150
+ let lineOffset = 0;
151
+ let marked = false;
152
+ const fail = (message, offset) => {
153
+ const { line, column } = positionOf(starts, offset);
154
+ throw declarationError(message, line, column);
155
+ };
156
+ const push = (kind, start, end, extra = {}) => {
157
+ const { line, column } = positionOf(starts, start);
158
+ tokens.push({
159
+ kind: kind,
160
+ text: extra.text ?? text.slice(start, end),
161
+ value: extra.value,
162
+ float: extra.float,
163
+ line: line,
164
+ column: column,
165
+ ...marked ? { file: file, fileLine: line + lineOffset } : {},
166
+ ...extra.source !== undefined ? { source: extra.source } : {}
167
+ });
168
+ };
169
+ while (i < length) {
170
+ const char = text[i];
171
+ //Whitespace, the continuations of lines
172
+ if (char === '\n') {
173
+ lineStart = true;
174
+ i++;
175
+ continue;
176
+ }
177
+ if (char === ' ' || char === '\t' || char === '\r' || char === '\f' || char === '\v') {
178
+ i++;
179
+ continue;
180
+ }
181
+ if (char === '\\' && (text[i + 1] === '\n' || (text[i + 1] === '\r' && text[i + 2] === '\n'))) {
182
+ i += text[i + 1] === '\n' ? 2 : 3;
183
+ continue;
184
+ }
185
+ //Comments
186
+ if (char === '/' && text[i + 1] === '/') {
187
+ while (i < length && text[i] !== '\n')
188
+ i++;
189
+ continue;
190
+ }
191
+ if (char === '/' && text[i + 1] === '*') {
192
+ const end = text.indexOf('*/', i + 2);
193
+ if (end < 0)
194
+ fail('A comment is not closed', i);
195
+ i = end + 2;
196
+ continue;
197
+ }
198
+ //Directives of the preprocessor, at the start of a line
199
+ if (char === '#' && lineStart) {
200
+ const start = i;
201
+ const end = directiveEnd(text, i);
202
+ const directive = withoutComments(text.slice(start + 1, end));
203
+ const name = directive.match(/^[A-Za-z_]\w*/)?.[0] ?? '';
204
+ if (CONDITIONALS.includes(name))
205
+ fail(`The directive #${name} is not supported: run the header through the preprocessor first (cc -E, cl /P)`, start);
206
+ const marker = name === '' || name === 'line' ? lineMarker(directive) : undefined;
207
+ if (marker) {
208
+ marked = true;
209
+ if (marker.file !== undefined)
210
+ file = marker.file;
211
+ lineOffset = marker.line - (positionOf(starts, start).line + 1);
212
+ }
213
+ const pack = directive.match(/^pragma\s+pack\s*\(([^)]*)\)/);
214
+ if (pack)
215
+ push('pragma', start, end, { text: `pack(${pack[1].replace(/\s+/g, '')})` });
216
+ //The text of a macro after its name, its comments removed; the name directly followed by ( takes arguments
217
+ if (name === 'define' || name === 'undef')
218
+ push(name, start, end, { text: directive.slice(name.length).trim() });
219
+ i = end;
220
+ continue;
221
+ }
222
+ lineStart = false;
223
+ //Identifiers, and the prefixes of the wide strings and characters
224
+ if (/[A-Za-z_$]/.test(char)) {
225
+ let end = i + 1;
226
+ while (end < length && /[\w$]/.test(text[end]))
227
+ end++;
228
+ const word = text.slice(i, end);
229
+ if ((text[end] === '\'' || text[end] === '"') && ['L', 'u', 'U', 'u8'].includes(word)) {
230
+ i = end;
231
+ continue;
232
+ }
233
+ push('identifier', i, end);
234
+ i = end;
235
+ continue;
236
+ }
237
+ //Numbers
238
+ if (/[0-9]/.test(char) || (char === '.' && /[0-9]/.test(text[i + 1] ?? ''))) {
239
+ let end = i;
240
+ const hex = /^0[xX]/.test(text.slice(i, i + 2));
241
+ if (hex)
242
+ end += 2;
243
+ while (end < length && (/[\w.]/.test(text[end]) || (/[eEpP]/.test(text[end - 1]) && /[+-]/.test(text[end]) && (!hex || /[pP]/.test(text[end - 1])))))
244
+ end++;
245
+ const literal = text.slice(i, end);
246
+ //The suffixes of C, and the ones of Microsoft (i64, ui64, I32…)
247
+ const body = literal.replace(/(?:[uU](?:ll|LL|l|L)?|(?:ll|LL|l|L)[uU]?|[uU]?[iI](?:8|16|32|64))$/, '');
248
+ const float = hex ? /[.pP]/.test(body) : /[.eE]/.test(body) || /[fF]$/.test(literal);
249
+ if (float) {
250
+ push('number', i, end, { float: true });
251
+ }
252
+ else {
253
+ let value;
254
+ try {
255
+ if (/^0[bB]/.test(body))
256
+ value = BigInt(body);
257
+ else if (/^0[0-7]+$/.test(body))
258
+ value = BigInt(`0o${body.slice(1)}`);
259
+ else
260
+ value = BigInt(body);
261
+ }
262
+ catch {
263
+ return fail(`The number ${literal} is not valid`, i);
264
+ }
265
+ push('number', i, end, { value: value });
266
+ }
267
+ i = end;
268
+ continue;
269
+ }
270
+ //Characters and strings
271
+ if (char === '\'' || char === '"') {
272
+ let end = i + 1;
273
+ const codes = [];
274
+ while (end < length && text[end] !== char) {
275
+ if (text[end] === '\n')
276
+ fail(char === '"' ? 'A string is not closed' : 'A character is not closed', i);
277
+ if (text[end] === '\\') {
278
+ const next = text[end + 1];
279
+ if (next === 'x') {
280
+ const digits = text.slice(end + 2).match(/^[0-9a-fA-F]+/)?.[0] ?? '';
281
+ codes.push(parseInt(digits || '0', 16));
282
+ end += 2 + digits.length;
283
+ }
284
+ else if (/[0-7]/.test(next)) {
285
+ const digits = text.slice(end + 1).match(/^[0-7]{1,3}/)[0];
286
+ codes.push(parseInt(digits, 8));
287
+ end += 1 + digits.length;
288
+ }
289
+ else {
290
+ codes.push(ESCAPES[next] ?? next.charCodeAt(0));
291
+ end += 2;
292
+ }
293
+ }
294
+ else {
295
+ codes.push(text.codePointAt(end));
296
+ end += text.codePointAt(end) > 0xFFFF ? 2 : 1;
297
+ }
298
+ }
299
+ if (end >= length)
300
+ fail(char === '"' ? 'A string is not closed' : 'A character is not closed', i);
301
+ if (char === '\'')
302
+ push('char', i, end + 1, { value: BigInt(codes[0] ?? 0) });
303
+ else
304
+ push('string', i, end + 1, { text: String.fromCodePoint(...codes), source: text.slice(i, end + 1) });
305
+ i = end + 1;
306
+ continue;
307
+ }
308
+ //Punctuators
309
+ const punctuator = PUNCTUATORS.find((candidate) => text.startsWith(candidate, i));
310
+ if (punctuator) {
311
+ push('punctuator', i, i + punctuator.length);
312
+ i += punctuator.length;
313
+ continue;
314
+ }
315
+ if (SINGLE.includes(char)) {
316
+ push('punctuator', i, i + 1);
317
+ i++;
318
+ continue;
319
+ }
320
+ fail(`Unexpected character ${JSON.stringify(char)}`, i);
321
+ }
322
+ const { line, column } = positionOf(starts, length);
323
+ tokens.push({ kind: 'end', text: '', line: line, column: column });
324
+ return tokens;
325
+ }
@@ -0,0 +1,90 @@
1
+ import { type Token } from './Lexer.js';
2
+ import type { Platform } from './Platform.js';
3
+ import { type CType, type Declarations, type FunctionDeclaration } from './Types.js';
4
+ /**
5
+ * A declaration of a preprocessed header, as readSource reads it (for the generator of libraries)
6
+ */
7
+ export interface SourceDeclaration {
8
+ /**
9
+ * The file it comes from, and its line there (from the line markers of the preprocessor)
10
+ */
11
+ readonly file?: string;
12
+ readonly line?: number;
13
+ /**
14
+ * Its tokens, the macros expanded
15
+ */
16
+ readonly tokens: readonly Token[];
17
+ /**
18
+ * What it declares: `type name`, `struct tag`, `union tag`, `enum tag` (defined, not only named), `function name`,
19
+ * `variable name`, `constant name`
20
+ */
21
+ readonly names: readonly string[];
22
+ /**
23
+ * The packing of #pragma pack it was read with
24
+ */
25
+ readonly pack?: number;
26
+ /**
27
+ * Why it could not be read
28
+ */
29
+ readonly error?: string;
30
+ }
31
+ /**
32
+ * Where a macro is defined, in a preprocessed header
33
+ */
34
+ export interface MacroOrigin {
35
+ readonly file?: string;
36
+ readonly line?: number;
37
+ }
38
+ /**
39
+ * A preprocessed header read by readSource: its declarations, each one as it is written, and where its macros are
40
+ */
41
+ export interface SourceDeclarations {
42
+ readonly declarations: Declarations;
43
+ readonly items: readonly SourceDeclaration[];
44
+ readonly macros: ReadonlyMap<string, MacroOrigin>;
45
+ }
46
+ /**
47
+ * Reads C declarations: typedefs, structs, unions, enums, function prototypes and variables, for one platform. Each
48
+ * library has its own declarations: their names do not meet the ones of another library
49
+ */
50
+ export declare class DeclarationParser {
51
+ #private;
52
+ /**
53
+ * Read declarations for a platform.
54
+ * @param platform The platform: the sizes of the types and the layout of the structs depend on it.
55
+ * @param declarations Declarations already read, added to (the ones of the library, when a prototype is read).
56
+ */
57
+ constructor(platform: Platform, declarations?: Declarations);
58
+ /**
59
+ * The declarations read so far.
60
+ */
61
+ get declarations(): Declarations;
62
+ /**
63
+ * Read a text of declarations, adding what it declares.
64
+ * @param text The declarations, as a header writes them (preprocessed or not: #pragma pack is read, #define and
65
+ * #include are skipped, #if and its kind are refused).
66
+ */
67
+ parse(text: string): Declarations;
68
+ /**
69
+ * Read a preprocessed header for the generator of libraries: each declaration kept with the file it comes from and
70
+ * what it declares; one that cannot be read is skipped, its error kept, and the next ones are read.
71
+ * @param text The output of the preprocessor (`cc -E -dD`, `cl /P`): its line markers give the files.
72
+ * @returns The declarations, each declaration as it is written, and where the macros are defined.
73
+ */
74
+ readSource(text: string): SourceDeclarations;
75
+ /**
76
+ * Read the prototype of one function (a trailing semicolon is optional).
77
+ * @param text The prototype: `int sqlite3_open(const char *filename, _Out_ sqlite3 **db)`.
78
+ */
79
+ prototype(text: string): FunctionDeclaration;
80
+ /**
81
+ * Read a type name: `struct timeval`, `const char *`, `int[4]`.
82
+ * @param text The type.
83
+ */
84
+ typeName(text: string): CType;
85
+ /**
86
+ * The value of a constant expression of the declarations (`SQLITE_OK + 1`, `1 << 4`).
87
+ * @param text The expression.
88
+ */
89
+ constant(text: string): bigint;
90
+ }