@thi.ng/wasm-api 0.14.0 → 0.16.0
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/CHANGELOG.md +60 -1
- package/README.md +173 -108
- package/api.d.ts +126 -28
- package/api.js +10 -4
- package/bridge.d.ts +3 -29
- package/bridge.js +2 -28
- package/cli.js +8 -4
- package/codegen/align.d.ts +14 -0
- package/codegen/align.js +35 -0
- package/codegen/c11.js +71 -55
- package/codegen/typescript.js +34 -14
- package/codegen/utils.d.ts +20 -11
- package/codegen/utils.js +28 -9
- package/codegen/zig.d.ts +6 -1
- package/codegen/zig.js +99 -71
- package/codegen.d.ts +2 -1
- package/codegen.js +77 -54
- package/package.json +25 -21
- package/string.d.ts +4 -0
- package/string.js +4 -0
- package/zig/managed-index.zig +223 -0
- package/{include → zig}/wasmapi.zig +2 -0
package/api.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { BigType, FloatType, Fn, IDeref, ILength, IObjectOf } from "@thi.ng/api";
|
|
1
|
+
import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf } from "@thi.ng/api";
|
|
2
|
+
import type { Pow2 } from "@thi.ng/binary";
|
|
2
3
|
import type { WasmBridge } from "./bridge.js";
|
|
3
4
|
export declare const PKG_NAME = "@thi.ng/wasm-api";
|
|
4
5
|
export declare const EVENT_MEMORY_CHANGED = "memory-changed";
|
|
@@ -52,7 +53,7 @@ export interface WasmExports {
|
|
|
52
53
|
* @remarks
|
|
53
54
|
* #### Zig
|
|
54
55
|
*
|
|
55
|
-
* Using the supplied Zig bindings (see `/
|
|
56
|
+
* Using the supplied Zig bindings (see `/zig/wasmapi.zig`), it's the
|
|
56
57
|
* user's responsibility to define a public `WASM_ALLOCATOR` in the root
|
|
57
58
|
* source file to enable allocations, e.g. using the
|
|
58
59
|
* [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
|
|
@@ -80,6 +81,7 @@ export interface WasmExports {
|
|
|
80
81
|
*/
|
|
81
82
|
_wasm_free(addr: number, numBytes: number): void;
|
|
82
83
|
}
|
|
84
|
+
export declare type MemorySlice = [addr: number, len: number];
|
|
83
85
|
export interface IWasmMemoryAccess {
|
|
84
86
|
i8: Int8Array;
|
|
85
87
|
u8: Uint8Array;
|
|
@@ -96,6 +98,41 @@ export interface IWasmMemoryAccess {
|
|
|
96
98
|
* after growing the WASM memory and the previous buffer becoming detached).
|
|
97
99
|
*/
|
|
98
100
|
ensureMemory(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Attempts to grow the WASM memory by an additional `numPages` (64KB/page)
|
|
103
|
+
* and if successful updates all typed memory views to use the new
|
|
104
|
+
* underlying buffer.
|
|
105
|
+
*
|
|
106
|
+
* @param numPages
|
|
107
|
+
*/
|
|
108
|
+
growMemory(numPages: number): void;
|
|
109
|
+
/**
|
|
110
|
+
* Attempts to allocate `numBytes` using the exported WASM core API function
|
|
111
|
+
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
112
|
+
* start address of the new memory block. If unsuccessful, throws an
|
|
113
|
+
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
114
|
+
* be zero-filled.
|
|
115
|
+
*
|
|
116
|
+
* @remarks
|
|
117
|
+
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
118
|
+
*
|
|
119
|
+
* @param numBytes
|
|
120
|
+
* @param clear
|
|
121
|
+
*/
|
|
122
|
+
allocate(numBytes: number, clear?: boolean): MemorySlice;
|
|
123
|
+
/**
|
|
124
|
+
* Frees a previous allocated memory region using the exported WASM core API
|
|
125
|
+
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
126
|
+
* `numBytes` value must be the same as previously given to
|
|
127
|
+
* {@link IWasmMemoryAccess.allocate}.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* This function always succeeds, regardless of presence of an active
|
|
131
|
+
* allocator on the WASM side or validity of given arguments.
|
|
132
|
+
*
|
|
133
|
+
* @param slice
|
|
134
|
+
*/
|
|
135
|
+
free(slice: MemorySlice): void;
|
|
99
136
|
/**
|
|
100
137
|
* Reads UTF-8 encoded string from given address and optional byte length.
|
|
101
138
|
* The default length is 0, which will be interpreted as a zero-terminated
|
|
@@ -124,8 +161,12 @@ export interface IWasmMemoryAccess {
|
|
|
124
161
|
}
|
|
125
162
|
/**
|
|
126
163
|
* Core API of WASM imports defined by the {@link WasmBridge}. The same
|
|
127
|
-
* functions are declared as bindings in `/zig/
|
|
128
|
-
* documentation of each function
|
|
164
|
+
* functions are declared as bindings in `/zig/wasmapi.zig`. **Also see this
|
|
165
|
+
* file for documentation of each function...**
|
|
166
|
+
*
|
|
167
|
+
* @remarks
|
|
168
|
+
* Zig API:
|
|
169
|
+
* https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/wasmapi.zig
|
|
129
170
|
*/
|
|
130
171
|
export interface CoreAPI extends WebAssembly.ModuleImports {
|
|
131
172
|
printI8: Fn<number, void>;
|
|
@@ -192,17 +233,17 @@ export interface TypeInfo {
|
|
|
192
233
|
*/
|
|
193
234
|
__size?: number;
|
|
194
235
|
/**
|
|
195
|
-
* Auto-computed offset (in bytes) in parent struct
|
|
236
|
+
* Auto-computed offset (in bytes) in parent struct.
|
|
196
237
|
*
|
|
197
238
|
* @internal
|
|
198
239
|
*/
|
|
199
240
|
__offset?: number;
|
|
200
241
|
/**
|
|
201
|
-
* Auto-computed alignment (in bytes)
|
|
242
|
+
* Auto-computed alignment (in bytes) actually used.
|
|
202
243
|
*
|
|
203
244
|
* @internal
|
|
204
245
|
*/
|
|
205
|
-
__align?:
|
|
246
|
+
__align?: Pow2;
|
|
206
247
|
}
|
|
207
248
|
export interface TopLevelType extends TypeInfo {
|
|
208
249
|
/**
|
|
@@ -212,27 +253,32 @@ export interface TopLevelType extends TypeInfo {
|
|
|
212
253
|
/**
|
|
213
254
|
* Optional (multi-line) docstring for this type
|
|
214
255
|
*/
|
|
215
|
-
doc?: string;
|
|
256
|
+
doc?: string | string[];
|
|
216
257
|
/**
|
|
217
258
|
* Type / kind
|
|
218
259
|
*/
|
|
219
|
-
type: "struct" | "
|
|
260
|
+
type: "enum" | "struct" | "union";
|
|
220
261
|
/**
|
|
221
262
|
* Optional object of user provided source codes to be injected into the
|
|
222
|
-
* generated type
|
|
263
|
+
* generated type (after generated fields). Keys of this object are language
|
|
264
|
+
* IDs (`ts` for {@link TYPESCRIPT}, `zig` for {@link ZIG}).
|
|
223
265
|
*
|
|
224
266
|
* @remarks
|
|
225
|
-
* Currently only supported by the
|
|
267
|
+
* Currently only supported by the code gens mentioned, ignored otherwise.
|
|
226
268
|
*/
|
|
227
|
-
body?: IObjectOf<string>;
|
|
269
|
+
body?: IObjectOf<string | string[] | InjectedBody>;
|
|
270
|
+
}
|
|
271
|
+
export interface InjectedBody {
|
|
272
|
+
decl?: string | string[];
|
|
273
|
+
impl?: string | string[];
|
|
228
274
|
}
|
|
229
275
|
export interface Struct extends TopLevelType {
|
|
230
276
|
type: "struct";
|
|
231
277
|
/**
|
|
232
|
-
*
|
|
278
|
+
* Array of struct fields (might be re-ordered if {@link Struct.auto} is
|
|
233
279
|
* enabled).
|
|
234
280
|
*/
|
|
235
|
-
fields:
|
|
281
|
+
fields: Field[];
|
|
236
282
|
/**
|
|
237
283
|
* If true, struct fields will be re-ordered in descending order based on
|
|
238
284
|
* their {@link TypeInfo.__align} size. This might result in overall smaller
|
|
@@ -248,8 +294,31 @@ export interface Struct extends TopLevelType {
|
|
|
248
294
|
* interpretation, currently only used by {@link ZIG}).
|
|
249
295
|
*/
|
|
250
296
|
tag?: "extern" | "packed";
|
|
297
|
+
/**
|
|
298
|
+
* Optional user supplied {@link AlignStrategy}. By default uses
|
|
299
|
+
* {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" structs).
|
|
300
|
+
*/
|
|
301
|
+
align?: AlignStrategy;
|
|
302
|
+
}
|
|
303
|
+
export interface Union extends TopLevelType {
|
|
304
|
+
type: "union";
|
|
305
|
+
/**
|
|
306
|
+
* Array of union fields.
|
|
307
|
+
*/
|
|
308
|
+
fields: Field[];
|
|
309
|
+
/**
|
|
310
|
+
* Optional qualifier for the kind of struct to be emitted (codegen specific
|
|
311
|
+
* interpretation, currently only used by {@link ZIG}).
|
|
312
|
+
*/
|
|
313
|
+
tag?: "extern" | "packed";
|
|
314
|
+
/**
|
|
315
|
+
* Optional user supplied {@link AlignStrategy}. By default uses
|
|
316
|
+
* {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
|
|
317
|
+
*/
|
|
318
|
+
align?: AlignStrategy;
|
|
251
319
|
}
|
|
252
|
-
export
|
|
320
|
+
export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
|
|
321
|
+
export interface Field extends TypeInfo {
|
|
253
322
|
/**
|
|
254
323
|
* Field name (prefix: "__" is reserved)
|
|
255
324
|
*/
|
|
@@ -257,14 +326,14 @@ export interface StructField extends TypeInfo {
|
|
|
257
326
|
/**
|
|
258
327
|
* Field docstring (can be multiline, will be formatted)
|
|
259
328
|
*/
|
|
260
|
-
doc?: string;
|
|
329
|
+
doc?: string | string[];
|
|
261
330
|
/**
|
|
262
331
|
* Field type tag/qualifier (note: `slice` & `vec` are only supported by Zig
|
|
263
332
|
* & TS).
|
|
264
333
|
*
|
|
265
334
|
* @remarks
|
|
266
335
|
* - Array & vector fields are statically sized (using
|
|
267
|
-
* {@link
|
|
336
|
+
* {@link Field.len})
|
|
268
337
|
* - Pointers are emitted as single-value pointers (where this distinction
|
|
269
338
|
* exist), i.e. even if they're pointing to multiple values, there's no
|
|
270
339
|
* explicit length encoded/available
|
|
@@ -274,7 +343,7 @@ export interface StructField extends TypeInfo {
|
|
|
274
343
|
*
|
|
275
344
|
* @defaultValue "scalar"
|
|
276
345
|
*/
|
|
277
|
-
tag?:
|
|
346
|
+
tag?: FieldTag;
|
|
278
347
|
/**
|
|
279
348
|
* Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
|
|
280
349
|
* value is interpreted as another type name in the {@link TypeColl}.
|
|
@@ -297,7 +366,7 @@ export interface StructField extends TypeInfo {
|
|
|
297
366
|
*/
|
|
298
367
|
sentinel?: number;
|
|
299
368
|
/**
|
|
300
|
-
* Array or vector length (see {@link
|
|
369
|
+
* Array or vector length (see {@link Field.tag})
|
|
301
370
|
*/
|
|
302
371
|
len?: number;
|
|
303
372
|
/**
|
|
@@ -306,8 +375,9 @@ export interface StructField extends TypeInfo {
|
|
|
306
375
|
*/
|
|
307
376
|
default?: number;
|
|
308
377
|
/**
|
|
309
|
-
* If defined and > 0, the field will be considered for padding purposes
|
|
310
|
-
* the value provided is the number of bytes used.
|
|
378
|
+
* If defined and > 0, the field will be considered for padding purposes
|
|
379
|
+
* only and the value provided is the number of bytes used. All other config
|
|
380
|
+
* for this field will be ignored!
|
|
311
381
|
*/
|
|
312
382
|
pad?: number;
|
|
313
383
|
}
|
|
@@ -340,22 +410,42 @@ export interface EnumValue {
|
|
|
340
410
|
*/
|
|
341
411
|
doc?: string;
|
|
342
412
|
}
|
|
413
|
+
export interface AlignStrategy {
|
|
414
|
+
/**
|
|
415
|
+
* Returns implementation specific alignment for given struct field.
|
|
416
|
+
*/
|
|
417
|
+
align: Fn<Field, Pow2>;
|
|
418
|
+
/**
|
|
419
|
+
* Returns possibly rounded value for given base size & alignment.
|
|
420
|
+
*/
|
|
421
|
+
size: Fn2<number, Pow2, number>;
|
|
422
|
+
/**
|
|
423
|
+
* Returns possibly rounded value for given base offset & alignment.
|
|
424
|
+
*/
|
|
425
|
+
offset: Fn2<number, Pow2, number>;
|
|
426
|
+
}
|
|
343
427
|
export interface CodeGenOptsBase {
|
|
344
428
|
/**
|
|
345
429
|
* Optional string to be injected before generated type defs (but after
|
|
346
430
|
* codegen's own prelude, if any)
|
|
347
431
|
*/
|
|
348
|
-
pre
|
|
432
|
+
pre?: string;
|
|
349
433
|
/**
|
|
350
434
|
* Optional string to be injected after generated type defs (but before
|
|
351
435
|
* codegen's own epilogue, if any)
|
|
352
436
|
*/
|
|
353
|
-
post
|
|
437
|
+
post?: string;
|
|
354
438
|
}
|
|
355
439
|
/**
|
|
356
440
|
* Global/shared code generator options.
|
|
357
441
|
*/
|
|
358
442
|
export interface CodeGenOpts extends CodeGenOptsBase {
|
|
443
|
+
/**
|
|
444
|
+
* WASM target specification.
|
|
445
|
+
*
|
|
446
|
+
* @defaultValue {@link WASM32}
|
|
447
|
+
*/
|
|
448
|
+
target: WasmTarget;
|
|
359
449
|
/**
|
|
360
450
|
* Identifier how strings are stored on WASM side, e.g. in Zig string
|
|
361
451
|
* literals are slices (8 bytes), in C just plain pointers (4 bytes).
|
|
@@ -400,7 +490,7 @@ export interface ICodeGen {
|
|
|
400
490
|
/**
|
|
401
491
|
* Docstring codegen
|
|
402
492
|
*/
|
|
403
|
-
doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
|
|
493
|
+
doc: (doc: string | string[], acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
|
|
404
494
|
/**
|
|
405
495
|
* Codegen for enum types.
|
|
406
496
|
*/
|
|
@@ -409,13 +499,21 @@ export interface ICodeGen {
|
|
|
409
499
|
* Codegen for struct types.
|
|
410
500
|
*/
|
|
411
501
|
struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
502
|
+
/**
|
|
503
|
+
* Codegen for union types.
|
|
504
|
+
*/
|
|
505
|
+
union: (type: Union, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
506
|
+
}
|
|
507
|
+
export interface WasmTarget {
|
|
508
|
+
usize: "u32" | "u64";
|
|
509
|
+
usizeBytes: number;
|
|
412
510
|
}
|
|
413
511
|
/**
|
|
414
|
-
*
|
|
512
|
+
* WASM32 target spec
|
|
415
513
|
*/
|
|
416
|
-
export declare const
|
|
514
|
+
export declare const WASM32: WasmTarget;
|
|
417
515
|
/**
|
|
418
|
-
*
|
|
516
|
+
* WASM64 target spec
|
|
419
517
|
*/
|
|
420
|
-
export declare const
|
|
518
|
+
export declare const WASM64: WasmTarget;
|
|
421
519
|
//# sourceMappingURL=api.d.ts.map
|
package/api.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export const PKG_NAME = "@thi.ng/wasm-api";
|
|
2
2
|
export const EVENT_MEMORY_CHANGED = "memory-changed";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* WASM32 target spec
|
|
5
5
|
*/
|
|
6
|
-
export const
|
|
6
|
+
export const WASM32 = {
|
|
7
|
+
usize: "u32",
|
|
8
|
+
usizeBytes: 4,
|
|
9
|
+
};
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
11
|
+
* WASM64 target spec
|
|
9
12
|
*/
|
|
10
|
-
export const
|
|
13
|
+
export const WASM64 = {
|
|
14
|
+
usize: "u64",
|
|
15
|
+
usizeBytes: 8,
|
|
16
|
+
};
|
package/bridge.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
|
|
3
3
|
import type { ILogger } from "@thi.ng/logger";
|
|
4
|
-
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
|
|
4
|
+
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, MemorySlice, WasmExports } from "./api.js";
|
|
5
5
|
export declare const Panic: {
|
|
6
6
|
new (msg?: string | undefined): {
|
|
7
7
|
name: string;
|
|
@@ -136,34 +136,8 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
136
136
|
* @param numPages
|
|
137
137
|
*/
|
|
138
138
|
growMemory(numPages: number): void;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
142
|
-
* start address of the new memory block. If unsuccessful, throws an
|
|
143
|
-
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
144
|
-
* be zero-filled.
|
|
145
|
-
*
|
|
146
|
-
* @remarks
|
|
147
|
-
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
148
|
-
*
|
|
149
|
-
* @param numBytes
|
|
150
|
-
* @param clear
|
|
151
|
-
*/
|
|
152
|
-
allocate(numBytes: number, clear?: boolean): number;
|
|
153
|
-
/**
|
|
154
|
-
* Frees a previous allocated memory region using the exported WASM core API
|
|
155
|
-
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
156
|
-
* `numBytes` value must be the same as previously given to
|
|
157
|
-
* {@link WasmBridge.allocate}.
|
|
158
|
-
*
|
|
159
|
-
* @remarks
|
|
160
|
-
* This function always succeeds, regardless of presence of an active
|
|
161
|
-
* allocator on the WASM side or validity of given arguments.
|
|
162
|
-
*
|
|
163
|
-
* @param addr
|
|
164
|
-
* @param numBytes
|
|
165
|
-
*/
|
|
166
|
-
free(addr: number, numBytes: number): void;
|
|
139
|
+
allocate(numBytes: number, clear?: boolean): MemorySlice;
|
|
140
|
+
free([addr, numBytes]: MemorySlice): void;
|
|
167
141
|
getI8(addr: number): number;
|
|
168
142
|
getU8(addr: number): number;
|
|
169
143
|
getI16(addr: number): number;
|
package/bridge.js
CHANGED
|
@@ -198,19 +198,6 @@ let WasmBridge = class WasmBridge {
|
|
|
198
198
|
this.exports.memory.grow(numPages);
|
|
199
199
|
this.ensureMemory();
|
|
200
200
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Attempts to allocate `numBytes` using the exported WASM core API function
|
|
203
|
-
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
204
|
-
* start address of the new memory block. If unsuccessful, throws an
|
|
205
|
-
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
206
|
-
* be zero-filled.
|
|
207
|
-
*
|
|
208
|
-
* @remarks
|
|
209
|
-
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
210
|
-
*
|
|
211
|
-
* @param numBytes
|
|
212
|
-
* @param clear
|
|
213
|
-
*/
|
|
214
201
|
allocate(numBytes, clear = false) {
|
|
215
202
|
const addr = this.exports._wasm_allocate(numBytes);
|
|
216
203
|
if (!addr)
|
|
@@ -218,22 +205,9 @@ let WasmBridge = class WasmBridge {
|
|
|
218
205
|
this.logger.fine(() => `allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
219
206
|
this.ensureMemory();
|
|
220
207
|
clear && this.u8.fill(0, addr, addr + numBytes);
|
|
221
|
-
return addr;
|
|
208
|
+
return [addr, numBytes];
|
|
222
209
|
}
|
|
223
|
-
|
|
224
|
-
* Frees a previous allocated memory region using the exported WASM core API
|
|
225
|
-
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
226
|
-
* `numBytes` value must be the same as previously given to
|
|
227
|
-
* {@link WasmBridge.allocate}.
|
|
228
|
-
*
|
|
229
|
-
* @remarks
|
|
230
|
-
* This function always succeeds, regardless of presence of an active
|
|
231
|
-
* allocator on the WASM side or validity of given arguments.
|
|
232
|
-
*
|
|
233
|
-
* @param addr
|
|
234
|
-
* @param numBytes
|
|
235
|
-
*/
|
|
236
|
-
free(addr, numBytes) {
|
|
210
|
+
free([addr, numBytes]) {
|
|
237
211
|
this.logger.fine(() => `freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
238
212
|
this.exports._wasm_free(addr, numBytes);
|
|
239
213
|
}
|
package/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
|
|
2
|
-
import { isArray, isPlainObject } from "@thi.ng/checks";
|
|
2
|
+
import { isArray, isPlainObject, isString } from "@thi.ng/checks";
|
|
3
3
|
import { illegalArgs } from "@thi.ng/errors";
|
|
4
4
|
import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
|
|
5
5
|
import { ConsoleLogger } from "@thi.ng/logger";
|
|
@@ -22,7 +22,11 @@ const argOpts = {
|
|
|
22
22
|
hint: "FILE",
|
|
23
23
|
desc: "JSON config file with codegen options",
|
|
24
24
|
}),
|
|
25
|
-
debug: flag({
|
|
25
|
+
debug: flag({
|
|
26
|
+
alias: "d",
|
|
27
|
+
default: false,
|
|
28
|
+
desc: "enable debug output & functions",
|
|
29
|
+
}),
|
|
26
30
|
dryRun: flag({
|
|
27
31
|
default: false,
|
|
28
32
|
desc: "enable dry run (don't overwrite files)",
|
|
@@ -71,7 +75,7 @@ const invalidSpec = (path, msg) => {
|
|
|
71
75
|
const addTypeSpec = (ctx, path, coll, spec) => {
|
|
72
76
|
if (!(spec.name && spec.type))
|
|
73
77
|
invalidSpec(path);
|
|
74
|
-
if (!
|
|
78
|
+
if (!["enum", "struct", "union"].includes(spec.type))
|
|
75
79
|
invalidSpec(path, `${spec.name} type: ${spec.type}`);
|
|
76
80
|
if (coll[spec.name])
|
|
77
81
|
invalidSpec(path, `duplicate name: ${spec.name}`);
|
|
@@ -80,7 +84,7 @@ const addTypeSpec = (ctx, path, coll, spec) => {
|
|
|
80
84
|
invalidSpec(path, `${spec.name}.body must be an object`);
|
|
81
85
|
for (let lang in spec.body) {
|
|
82
86
|
const src = spec.body[lang];
|
|
83
|
-
if (src[0] === "@") {
|
|
87
|
+
if (isString(src) && src[0] === "@") {
|
|
84
88
|
spec.body[lang] = readText(src.substring(1), ctx.logger);
|
|
85
89
|
}
|
|
86
90
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AlignStrategy, TopLevelType } from "../api.js";
|
|
2
|
+
/**
|
|
3
|
+
* C ABI compatible alignment
|
|
4
|
+
*/
|
|
5
|
+
export declare const ALIGN_C: AlignStrategy;
|
|
6
|
+
export declare const ALIGN_PACKED: AlignStrategy;
|
|
7
|
+
/**
|
|
8
|
+
* Returns a suitable alignment strategy for given type, either via user
|
|
9
|
+
* supplied impl defined for the type or derived via a struct's tag.
|
|
10
|
+
*
|
|
11
|
+
* @param type
|
|
12
|
+
*/
|
|
13
|
+
export declare const selectAlignment: (type: TopLevelType) => AlignStrategy;
|
|
14
|
+
//# sourceMappingURL=align.d.ts.map
|
package/codegen/align.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SIZEOF } from "@thi.ng/api/typedarray";
|
|
2
|
+
import { align as $align } from "@thi.ng/binary/align";
|
|
3
|
+
import { ceilPow2 } from "@thi.ng/binary/pow";
|
|
4
|
+
/**
|
|
5
|
+
* C ABI compatible alignment
|
|
6
|
+
*/
|
|
7
|
+
export const ALIGN_C = {
|
|
8
|
+
align: (field) => {
|
|
9
|
+
let align = SIZEOF[field.type];
|
|
10
|
+
if (field.tag === "vec") {
|
|
11
|
+
align *= ceilPow2(field.len);
|
|
12
|
+
}
|
|
13
|
+
return align;
|
|
14
|
+
},
|
|
15
|
+
size: (size, align) => $align(size, align),
|
|
16
|
+
offset: (offset, align) => $align(offset, align),
|
|
17
|
+
};
|
|
18
|
+
export const ALIGN_PACKED = {
|
|
19
|
+
align: () => 1,
|
|
20
|
+
size: (size) => size,
|
|
21
|
+
offset: (offset) => offset,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns a suitable alignment strategy for given type, either via user
|
|
25
|
+
* supplied impl defined for the type or derived via a struct's tag.
|
|
26
|
+
*
|
|
27
|
+
* @param type
|
|
28
|
+
*/
|
|
29
|
+
export const selectAlignment = (type) => {
|
|
30
|
+
if (type.type === "struct" || type.type === "union") {
|
|
31
|
+
let $type = type;
|
|
32
|
+
return $type.align || ($type.tag === "packed" ? ALIGN_PACKED : ALIGN_C);
|
|
33
|
+
}
|
|
34
|
+
return ALIGN_C;
|
|
35
|
+
};
|
package/codegen/c11.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isString } from "@thi.ng/checks/is-string";
|
|
2
2
|
import { unsupported } from "@thi.ng/errors/unsupported";
|
|
3
|
-
import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
|
|
3
|
+
import { enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
|
|
4
4
|
const PRIM_ALIASES = {
|
|
5
5
|
i8: "int8_t",
|
|
6
6
|
u8: "uint8_t",
|
|
@@ -68,62 +68,78 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
|
|
|
68
68
|
},
|
|
69
69
|
struct: (struct, coll, acc, opts) => {
|
|
70
70
|
const name = typePrefix + struct.name;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
? isStringSlice(opts.stringType)
|
|
85
|
-
? __slice("char", fconst)
|
|
86
|
-
: `${f.const !== false ? "const " : ""}char*`
|
|
87
|
-
: PRIM_ALIASES[f.type] || f.type;
|
|
88
|
-
if (coll[ftype])
|
|
89
|
-
ftype = typePrefix + ftype;
|
|
90
|
-
switch (f.tag) {
|
|
91
|
-
case "array":
|
|
92
|
-
case "vec":
|
|
93
|
-
res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
|
|
94
|
-
ftype = `${ftype}[${f.len}]`;
|
|
95
|
-
break;
|
|
96
|
-
case "slice":
|
|
97
|
-
ftype = __slice(ftype, fconst);
|
|
98
|
-
res.push(`${ftype} ${f.name};`);
|
|
99
|
-
break;
|
|
100
|
-
case "ptr":
|
|
101
|
-
ftype = `${fconst}${ftype}*`;
|
|
102
|
-
res.push(`${ftype} ${f.name};`);
|
|
103
|
-
break;
|
|
104
|
-
case "scalar":
|
|
105
|
-
default:
|
|
106
|
-
res.push(`${ftype} ${f.name};`);
|
|
107
|
-
}
|
|
108
|
-
ftypes[f.name] = ftype;
|
|
109
|
-
}
|
|
110
|
-
res.push("};");
|
|
111
|
-
if (opts.debug) {
|
|
112
|
-
const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
|
|
113
|
-
fn("align", `alignof(${name})`);
|
|
114
|
-
fn("size", `sizeof(${name})`);
|
|
115
|
-
for (let f of struct.fields) {
|
|
116
|
-
if (isPadding(f))
|
|
117
|
-
continue;
|
|
118
|
-
fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
|
|
119
|
-
fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
|
|
120
|
-
fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
res.push("");
|
|
124
|
-
acc.push(...withIndentation(res, INDENT, ...SCOPES));
|
|
71
|
+
acc.push(...withIndentation([
|
|
72
|
+
`typedef struct ${name} ${name};`,
|
|
73
|
+
`struct ${name} {`,
|
|
74
|
+
...__generateFields(gen, struct, coll, opts, typePrefix),
|
|
75
|
+
], INDENT, ...SCOPES));
|
|
76
|
+
},
|
|
77
|
+
union: (union, coll, acc, opts) => {
|
|
78
|
+
const name = typePrefix + union.name;
|
|
79
|
+
acc.push(...withIndentation([
|
|
80
|
+
`typedef union ${name} ${name};`,
|
|
81
|
+
`union ${name} {`,
|
|
82
|
+
...__generateFields(gen, union, coll, opts, typePrefix),
|
|
83
|
+
], INDENT, ...SCOPES));
|
|
125
84
|
},
|
|
126
85
|
};
|
|
127
86
|
return gen;
|
|
128
87
|
};
|
|
88
|
+
const __generateFields = (gen, parent, coll, opts, typePrefix) => {
|
|
89
|
+
const res = [];
|
|
90
|
+
const ftypes = {};
|
|
91
|
+
const isUnion = parent.type === "union";
|
|
92
|
+
const name = typePrefix + parent.name;
|
|
93
|
+
let padID = 0;
|
|
94
|
+
for (let f of parent.fields) {
|
|
95
|
+
// autolabel explicit padding fields
|
|
96
|
+
if (isPadding(f)) {
|
|
97
|
+
res.push(`uint8_t __pad${padID++}[${f.pad}];`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
f.doc && gen.doc(f.doc, res, opts);
|
|
101
|
+
const fconst = f.const ? "const " : "";
|
|
102
|
+
let ftype = isWasmString(f.type)
|
|
103
|
+
? isStringSlice(opts.stringType)
|
|
104
|
+
? __slice("char", fconst)
|
|
105
|
+
: `${f.const !== false ? "const " : ""}char*`
|
|
106
|
+
: PRIM_ALIASES[f.type] || f.type;
|
|
107
|
+
if (coll[ftype])
|
|
108
|
+
ftype = typePrefix + ftype;
|
|
109
|
+
switch (f.tag) {
|
|
110
|
+
case "array":
|
|
111
|
+
case "vec":
|
|
112
|
+
res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
|
|
113
|
+
ftype = `${ftype}[${f.len}]`;
|
|
114
|
+
break;
|
|
115
|
+
case "slice":
|
|
116
|
+
ftype = __slice(ftype, fconst);
|
|
117
|
+
res.push(`${ftype} ${f.name};`);
|
|
118
|
+
break;
|
|
119
|
+
case "ptr":
|
|
120
|
+
ftype = `${fconst}${ftype}*`;
|
|
121
|
+
res.push(`${ftype} ${f.name};`);
|
|
122
|
+
break;
|
|
123
|
+
case "scalar":
|
|
124
|
+
default:
|
|
125
|
+
res.push(`${ftype} ${f.name};`);
|
|
126
|
+
}
|
|
127
|
+
ftypes[f.name] = ftype;
|
|
128
|
+
}
|
|
129
|
+
res.push("};");
|
|
130
|
+
if (opts.debug) {
|
|
131
|
+
const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
|
|
132
|
+
fn("align", `alignof(${name})`);
|
|
133
|
+
fn("size", `sizeof(${name})`);
|
|
134
|
+
for (let f of parent.fields) {
|
|
135
|
+
if (isPadding(f))
|
|
136
|
+
continue;
|
|
137
|
+
fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
|
|
138
|
+
!isUnion && fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
|
|
139
|
+
fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
res.push("");
|
|
143
|
+
return res;
|
|
144
|
+
};
|
|
129
145
|
const __slice = (type, $const) => `struct { ${$const}${type} *ptr; size_t len; }`;
|