@thi.ng/wasm-api 0.13.1 → 0.15.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 +48 -1
- package/README.md +173 -108
- package/api.d.ts +83 -21
- package/api.js +10 -4
- package/bridge.js +1 -0
- package/cli.js +6 -2
- package/codegen/align.d.ts +14 -0
- package/codegen/align.js +35 -0
- package/codegen/c11.js +71 -55
- package/codegen/typescript.js +14 -11
- package/codegen/utils.d.ts +12 -4
- package/codegen/utils.js +8 -0
- package/codegen/zig.js +93 -69
- package/codegen.d.ts +2 -1
- package/codegen.js +77 -54
- package/package.json +19 -14
- package/string.d.ts +4 -0
- package/string.js +14 -3
- package/zig/managed-index.zig +223 -0
- package/{include → zig}/wasmapi.zig +43 -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";
|
|
@@ -91,6 +92,11 @@ export interface IWasmMemoryAccess {
|
|
|
91
92
|
u64: BigUint64Array;
|
|
92
93
|
f32: Float32Array;
|
|
93
94
|
f64: Float64Array;
|
|
95
|
+
/**
|
|
96
|
+
* Initializes and/or updates the various typed WASM memory views (e.g.
|
|
97
|
+
* after growing the WASM memory and the previous buffer becoming detached).
|
|
98
|
+
*/
|
|
99
|
+
ensureMemory(): void;
|
|
94
100
|
/**
|
|
95
101
|
* Reads UTF-8 encoded string from given address and optional byte length.
|
|
96
102
|
* The default length is 0, which will be interpreted as a zero-terminated
|
|
@@ -119,8 +125,12 @@ export interface IWasmMemoryAccess {
|
|
|
119
125
|
}
|
|
120
126
|
/**
|
|
121
127
|
* Core API of WASM imports defined by the {@link WasmBridge}. The same
|
|
122
|
-
* functions are declared as bindings in `/zig/
|
|
123
|
-
* documentation of each function
|
|
128
|
+
* functions are declared as bindings in `/zig/wasmapi.zig`. **Also see this
|
|
129
|
+
* file for documentation of each function...**
|
|
130
|
+
*
|
|
131
|
+
* @remarks
|
|
132
|
+
* Zig API:
|
|
133
|
+
* https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/wasmapi.zig
|
|
124
134
|
*/
|
|
125
135
|
export interface CoreAPI extends WebAssembly.ModuleImports {
|
|
126
136
|
printI8: Fn<number, void>;
|
|
@@ -187,17 +197,17 @@ export interface TypeInfo {
|
|
|
187
197
|
*/
|
|
188
198
|
__size?: number;
|
|
189
199
|
/**
|
|
190
|
-
* Auto-computed offset (in bytes) in parent struct
|
|
200
|
+
* Auto-computed offset (in bytes) in parent struct.
|
|
191
201
|
*
|
|
192
202
|
* @internal
|
|
193
203
|
*/
|
|
194
204
|
__offset?: number;
|
|
195
205
|
/**
|
|
196
|
-
* Auto-computed alignment (in bytes)
|
|
206
|
+
* Auto-computed alignment (in bytes) actually used.
|
|
197
207
|
*
|
|
198
208
|
* @internal
|
|
199
209
|
*/
|
|
200
|
-
__align?:
|
|
210
|
+
__align?: Pow2;
|
|
201
211
|
}
|
|
202
212
|
export interface TopLevelType extends TypeInfo {
|
|
203
213
|
/**
|
|
@@ -211,7 +221,7 @@ export interface TopLevelType extends TypeInfo {
|
|
|
211
221
|
/**
|
|
212
222
|
* Type / kind
|
|
213
223
|
*/
|
|
214
|
-
type: "struct" | "
|
|
224
|
+
type: "enum" | "struct" | "union";
|
|
215
225
|
/**
|
|
216
226
|
* Optional object of user provided source codes to be injected into the
|
|
217
227
|
* generated type. Keys are language IDs (same name as respective codegen).
|
|
@@ -224,10 +234,10 @@ export interface TopLevelType extends TypeInfo {
|
|
|
224
234
|
export interface Struct extends TopLevelType {
|
|
225
235
|
type: "struct";
|
|
226
236
|
/**
|
|
227
|
-
*
|
|
237
|
+
* Array of struct fields (might be re-ordered if {@link Struct.auto} is
|
|
228
238
|
* enabled).
|
|
229
239
|
*/
|
|
230
|
-
fields:
|
|
240
|
+
fields: Field[];
|
|
231
241
|
/**
|
|
232
242
|
* If true, struct fields will be re-ordered in descending order based on
|
|
233
243
|
* their {@link TypeInfo.__align} size. This might result in overall smaller
|
|
@@ -243,8 +253,14 @@ export interface Struct extends TopLevelType {
|
|
|
243
253
|
* interpretation, currently only used by {@link ZIG}).
|
|
244
254
|
*/
|
|
245
255
|
tag?: "extern" | "packed";
|
|
256
|
+
/**
|
|
257
|
+
* Optional user supplied {@link AlignStrategy}. By default uses
|
|
258
|
+
* {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" structs).
|
|
259
|
+
*/
|
|
260
|
+
align?: AlignStrategy;
|
|
246
261
|
}
|
|
247
|
-
export
|
|
262
|
+
export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
|
|
263
|
+
export interface Field extends TypeInfo {
|
|
248
264
|
/**
|
|
249
265
|
* Field name (prefix: "__" is reserved)
|
|
250
266
|
*/
|
|
@@ -259,7 +275,7 @@ export interface StructField extends TypeInfo {
|
|
|
259
275
|
*
|
|
260
276
|
* @remarks
|
|
261
277
|
* - Array & vector fields are statically sized (using
|
|
262
|
-
* {@link
|
|
278
|
+
* {@link Field.len})
|
|
263
279
|
* - Pointers are emitted as single-value pointers (where this distinction
|
|
264
280
|
* exist), i.e. even if they're pointing to multiple values, there's no
|
|
265
281
|
* explicit length encoded/available
|
|
@@ -269,7 +285,7 @@ export interface StructField extends TypeInfo {
|
|
|
269
285
|
*
|
|
270
286
|
* @defaultValue "scalar"
|
|
271
287
|
*/
|
|
272
|
-
tag?:
|
|
288
|
+
tag?: FieldTag;
|
|
273
289
|
/**
|
|
274
290
|
* Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
|
|
275
291
|
* value is interpreted as another type name in the {@link TypeColl}.
|
|
@@ -292,7 +308,7 @@ export interface StructField extends TypeInfo {
|
|
|
292
308
|
*/
|
|
293
309
|
sentinel?: number;
|
|
294
310
|
/**
|
|
295
|
-
* Array or vector length (see {@link
|
|
311
|
+
* Array or vector length (see {@link Field.tag})
|
|
296
312
|
*/
|
|
297
313
|
len?: number;
|
|
298
314
|
/**
|
|
@@ -301,11 +317,29 @@ export interface StructField extends TypeInfo {
|
|
|
301
317
|
*/
|
|
302
318
|
default?: number;
|
|
303
319
|
/**
|
|
304
|
-
* If defined and > 0, the field will be considered for padding purposes
|
|
305
|
-
* the value provided is the number of bytes used.
|
|
320
|
+
* If defined and > 0, the field will be considered for padding purposes
|
|
321
|
+
* only and the value provided is the number of bytes used. All other config
|
|
322
|
+
* for this field will be ignored!
|
|
306
323
|
*/
|
|
307
324
|
pad?: number;
|
|
308
325
|
}
|
|
326
|
+
export interface Union extends TopLevelType {
|
|
327
|
+
type: "union";
|
|
328
|
+
/**
|
|
329
|
+
* Array of union fields.
|
|
330
|
+
*/
|
|
331
|
+
fields: Field[];
|
|
332
|
+
/**
|
|
333
|
+
* Optional qualifier for the kind of struct to be emitted (codegen specific
|
|
334
|
+
* interpretation, currently only used by {@link ZIG}).
|
|
335
|
+
*/
|
|
336
|
+
tag?: "extern" | "packed";
|
|
337
|
+
/**
|
|
338
|
+
* Optional user supplied {@link AlignStrategy}. By default uses
|
|
339
|
+
* {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
|
|
340
|
+
*/
|
|
341
|
+
align?: AlignStrategy;
|
|
342
|
+
}
|
|
309
343
|
export interface Enum extends TopLevelType {
|
|
310
344
|
type: "enum";
|
|
311
345
|
/**
|
|
@@ -335,22 +369,42 @@ export interface EnumValue {
|
|
|
335
369
|
*/
|
|
336
370
|
doc?: string;
|
|
337
371
|
}
|
|
372
|
+
export interface AlignStrategy {
|
|
373
|
+
/**
|
|
374
|
+
* Returns implementation specific alignment for given struct field.
|
|
375
|
+
*/
|
|
376
|
+
align: Fn<Field, Pow2>;
|
|
377
|
+
/**
|
|
378
|
+
* Returns possibly rounded value for given base size & alignment.
|
|
379
|
+
*/
|
|
380
|
+
size: Fn2<number, Pow2, number>;
|
|
381
|
+
/**
|
|
382
|
+
* Returns possibly rounded value for given base offset & alignment.
|
|
383
|
+
*/
|
|
384
|
+
offset: Fn2<number, Pow2, number>;
|
|
385
|
+
}
|
|
338
386
|
export interface CodeGenOptsBase {
|
|
339
387
|
/**
|
|
340
388
|
* Optional string to be injected before generated type defs (but after
|
|
341
389
|
* codegen's own prelude, if any)
|
|
342
390
|
*/
|
|
343
|
-
pre
|
|
391
|
+
pre?: string;
|
|
344
392
|
/**
|
|
345
393
|
* Optional string to be injected after generated type defs (but before
|
|
346
394
|
* codegen's own epilogue, if any)
|
|
347
395
|
*/
|
|
348
|
-
post
|
|
396
|
+
post?: string;
|
|
349
397
|
}
|
|
350
398
|
/**
|
|
351
399
|
* Global/shared code generator options.
|
|
352
400
|
*/
|
|
353
401
|
export interface CodeGenOpts extends CodeGenOptsBase {
|
|
402
|
+
/**
|
|
403
|
+
* WASM target specification.
|
|
404
|
+
*
|
|
405
|
+
* @defaultValue {@link WASM32}
|
|
406
|
+
*/
|
|
407
|
+
target: WasmTarget;
|
|
354
408
|
/**
|
|
355
409
|
* Identifier how strings are stored on WASM side, e.g. in Zig string
|
|
356
410
|
* literals are slices (8 bytes), in C just plain pointers (4 bytes).
|
|
@@ -404,13 +458,21 @@ export interface ICodeGen {
|
|
|
404
458
|
* Codegen for struct types.
|
|
405
459
|
*/
|
|
406
460
|
struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
461
|
+
/**
|
|
462
|
+
* Codegen for union types.
|
|
463
|
+
*/
|
|
464
|
+
union: (type: Union, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
465
|
+
}
|
|
466
|
+
export interface WasmTarget {
|
|
467
|
+
usize: "u32" | "u64";
|
|
468
|
+
usizeBytes: number;
|
|
407
469
|
}
|
|
408
470
|
/**
|
|
409
|
-
*
|
|
471
|
+
* WASM32 target spec
|
|
410
472
|
*/
|
|
411
|
-
export declare const
|
|
473
|
+
export declare const WASM32: WasmTarget;
|
|
412
474
|
/**
|
|
413
|
-
*
|
|
475
|
+
* WASM64 target spec
|
|
414
476
|
*/
|
|
415
|
-
export declare const
|
|
477
|
+
export declare const WASM64: WasmTarget;
|
|
416
478
|
//# 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.js
CHANGED
|
@@ -414,6 +414,7 @@ let WasmBridge = class WasmBridge {
|
|
|
414
414
|
* @param terminate
|
|
415
415
|
*/
|
|
416
416
|
setString(str, addr, maxBytes, terminate = true) {
|
|
417
|
+
this.ensureMemory();
|
|
417
418
|
maxBytes = Math.min(maxBytes, this.u8.length - addr);
|
|
418
419
|
const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
|
|
419
420
|
if (len == null || len >= maxBytes + (terminate ? 0 : 1)) {
|
package/cli.js
CHANGED
|
@@ -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}`);
|
|
@@ -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; }`;
|
package/codegen/typescript.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
|
|
2
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
|
-
import { PKG_NAME,
|
|
3
|
+
import { PKG_NAME, } from "../api.js";
|
|
4
4
|
import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
|
|
5
5
|
/**
|
|
6
6
|
* TypeScript code generator. Call with options and then pass to
|
|
@@ -91,7 +91,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
91
91
|
const fn = f.len
|
|
92
92
|
? [
|
|
93
93
|
`(addr) => {`,
|
|
94
|
-
...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
|
|
94
|
+
...__mapStringArray(opts.target, "buf", $stringImpl, f.len, isConst, true),
|
|
95
95
|
`}`,
|
|
96
96
|
]
|
|
97
97
|
: [
|
|
@@ -107,15 +107,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
else if (f.tag === "slice") {
|
|
110
|
-
lines.push(`const len = ${__ptr(offset + 4)};`);
|
|
110
|
+
lines.push(`const len = ${__ptr(opts.target, offset + 4)};`);
|
|
111
111
|
if (isPrim) {
|
|
112
|
-
lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
|
|
112
|
+
lines.push(`const addr = ${__ptrShift(opts.target, offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
|
|
113
113
|
}
|
|
114
114
|
else if (isStr) {
|
|
115
|
-
lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
|
|
115
|
+
lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapStringArray(opts.target, "buf", $stringImpl, "len", isConst));
|
|
116
116
|
}
|
|
117
117
|
else {
|
|
118
|
-
lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
|
|
118
|
+
lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapArray(f));
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
else if (f.tag === "array" || f.tag === "vec") {
|
|
@@ -123,7 +123,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
123
123
|
lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
|
|
124
124
|
}
|
|
125
125
|
else if (isStr) {
|
|
126
|
-
lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
|
|
126
|
+
lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(opts.target, f.name, $stringImpl, f.len, isConst));
|
|
127
127
|
}
|
|
128
128
|
else {
|
|
129
129
|
lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
|
|
@@ -162,6 +162,9 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
162
162
|
lines.push("};", "}", "});", "");
|
|
163
163
|
acc.push(...withIndentation(lines, indent, ...SCOPES));
|
|
164
164
|
},
|
|
165
|
+
union: (type, coll, acc, opts) => {
|
|
166
|
+
gen.struct(type, coll, acc, opts);
|
|
167
|
+
},
|
|
165
168
|
};
|
|
166
169
|
return gen;
|
|
167
170
|
};
|
|
@@ -201,9 +204,9 @@ const __addrShift = (offset, shift) => {
|
|
|
201
204
|
return __addr(offset) + (bits ? " >>> " + bits : "");
|
|
202
205
|
};
|
|
203
206
|
/** @internal */
|
|
204
|
-
const __ptr = (offset) => `mem.${
|
|
207
|
+
const __ptr = (target, offset) => `mem.${target.usize}[${__addrShift(offset, target.usize)}]`;
|
|
205
208
|
/** @internal */
|
|
206
|
-
const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
|
|
209
|
+
const __ptrShift = (target, offset, shift) => __ptr(target, offset) + " >>> " + __shift(shift);
|
|
207
210
|
const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
|
|
208
211
|
/** @internal */
|
|
209
212
|
const __mapArray = (f, len = "len") => [
|
|
@@ -213,8 +216,8 @@ const __mapArray = (f, len = "len") => [
|
|
|
213
216
|
`return slice;`,
|
|
214
217
|
];
|
|
215
218
|
/** @internal */
|
|
216
|
-
const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
|
|
219
|
+
const __mapStringArray = (target, name, type, len, isConst, isLocal = false) => [
|
|
217
220
|
isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`,
|
|
218
|
-
`for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${
|
|
221
|
+
`for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
|
|
219
222
|
`return $${name};`,
|
|
220
223
|
];
|
package/codegen/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BigType } from "@thi.ng/api";
|
|
2
|
-
import type { CodeGenOpts,
|
|
2
|
+
import type { CodeGenOpts, Field, WasmPrim, WasmPrim32 } from "../api.js";
|
|
3
3
|
/**
|
|
4
4
|
* Returns true iff `x` is a {@link WasmPrim32}.
|
|
5
5
|
*
|
|
@@ -19,7 +19,15 @@ export declare const isBigNumeric: (x: string) => x is BigType;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const isWasmPrim: (x: string) => x is WasmPrim;
|
|
21
21
|
export declare const isWasmString: (x: string) => x is "string";
|
|
22
|
-
export declare const isPadding: (f:
|
|
22
|
+
export declare const isPadding: (f: Field) => boolean;
|
|
23
|
+
export declare const isPointer: (f: Field) => boolean;
|
|
24
|
+
export declare const isSlice: (f: Field) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Returns true iff the struct field is a pointer, slice or "string" type
|
|
27
|
+
*
|
|
28
|
+
* @param f
|
|
29
|
+
*/
|
|
30
|
+
export declare const isPointerLike: (f: Field) => boolean;
|
|
23
31
|
/**
|
|
24
32
|
* Takes an array of strings or splits given string into lines, word wraps and
|
|
25
33
|
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
@@ -43,7 +51,7 @@ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is
|
|
|
43
51
|
*
|
|
44
52
|
* @internal
|
|
45
53
|
*/
|
|
46
|
-
export declare const pointerFields: (fields:
|
|
54
|
+
export declare const pointerFields: (fields: Field[]) => Field[];
|
|
47
55
|
/**
|
|
48
56
|
* Returns filtered array of struct fields of only "string" fields.
|
|
49
57
|
*
|
|
@@ -51,7 +59,7 @@ export declare const pointerFields: (fields: StructField[]) => StructField[];
|
|
|
51
59
|
*
|
|
52
60
|
* @internal
|
|
53
61
|
*/
|
|
54
|
-
export declare const stringFields: (fields:
|
|
62
|
+
export declare const stringFields: (fields: Field[]) => Field[];
|
|
55
63
|
/**
|
|
56
64
|
* Returns enum identifier formatted according to given opts.
|
|
57
65
|
*
|
package/codegen/utils.js
CHANGED
|
@@ -20,6 +20,14 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
|
|
|
20
20
|
export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
|
|
21
21
|
export const isWasmString = (x) => x === "string";
|
|
22
22
|
export const isPadding = (f) => f.pad != null && f.pad > 0;
|
|
23
|
+
export const isPointer = (f) => f.tag === "ptr";
|
|
24
|
+
export const isSlice = (f) => f.tag === "slice";
|
|
25
|
+
/**
|
|
26
|
+
* Returns true iff the struct field is a pointer, slice or "string" type
|
|
27
|
+
*
|
|
28
|
+
* @param f
|
|
29
|
+
*/
|
|
30
|
+
export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
|
|
23
31
|
/**
|
|
24
32
|
* Takes an array of strings or splits given string into lines, word wraps and
|
|
25
33
|
* then prefixes each line with given `width` and `prefix`. Returns array of new
|