@thi.ng/wasm-api 0.14.0 → 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 +36 -1
- package/README.md +173 -108
- package/api.d.ts +78 -21
- package/api.js +10 -4
- 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 +13 -9
- 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";
|
|
@@ -124,8 +125,12 @@ export interface IWasmMemoryAccess {
|
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
126
127
|
* 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
|
|
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
|
|
129
134
|
*/
|
|
130
135
|
export interface CoreAPI extends WebAssembly.ModuleImports {
|
|
131
136
|
printI8: Fn<number, void>;
|
|
@@ -192,17 +197,17 @@ export interface TypeInfo {
|
|
|
192
197
|
*/
|
|
193
198
|
__size?: number;
|
|
194
199
|
/**
|
|
195
|
-
* Auto-computed offset (in bytes) in parent struct
|
|
200
|
+
* Auto-computed offset (in bytes) in parent struct.
|
|
196
201
|
*
|
|
197
202
|
* @internal
|
|
198
203
|
*/
|
|
199
204
|
__offset?: number;
|
|
200
205
|
/**
|
|
201
|
-
* Auto-computed alignment (in bytes)
|
|
206
|
+
* Auto-computed alignment (in bytes) actually used.
|
|
202
207
|
*
|
|
203
208
|
* @internal
|
|
204
209
|
*/
|
|
205
|
-
__align?:
|
|
210
|
+
__align?: Pow2;
|
|
206
211
|
}
|
|
207
212
|
export interface TopLevelType extends TypeInfo {
|
|
208
213
|
/**
|
|
@@ -216,7 +221,7 @@ export interface TopLevelType extends TypeInfo {
|
|
|
216
221
|
/**
|
|
217
222
|
* Type / kind
|
|
218
223
|
*/
|
|
219
|
-
type: "struct" | "
|
|
224
|
+
type: "enum" | "struct" | "union";
|
|
220
225
|
/**
|
|
221
226
|
* Optional object of user provided source codes to be injected into the
|
|
222
227
|
* generated type. Keys are language IDs (same name as respective codegen).
|
|
@@ -229,10 +234,10 @@ export interface TopLevelType extends TypeInfo {
|
|
|
229
234
|
export interface Struct extends TopLevelType {
|
|
230
235
|
type: "struct";
|
|
231
236
|
/**
|
|
232
|
-
*
|
|
237
|
+
* Array of struct fields (might be re-ordered if {@link Struct.auto} is
|
|
233
238
|
* enabled).
|
|
234
239
|
*/
|
|
235
|
-
fields:
|
|
240
|
+
fields: Field[];
|
|
236
241
|
/**
|
|
237
242
|
* If true, struct fields will be re-ordered in descending order based on
|
|
238
243
|
* their {@link TypeInfo.__align} size. This might result in overall smaller
|
|
@@ -248,8 +253,14 @@ export interface Struct extends TopLevelType {
|
|
|
248
253
|
* interpretation, currently only used by {@link ZIG}).
|
|
249
254
|
*/
|
|
250
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;
|
|
251
261
|
}
|
|
252
|
-
export
|
|
262
|
+
export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
|
|
263
|
+
export interface Field extends TypeInfo {
|
|
253
264
|
/**
|
|
254
265
|
* Field name (prefix: "__" is reserved)
|
|
255
266
|
*/
|
|
@@ -264,7 +275,7 @@ export interface StructField extends TypeInfo {
|
|
|
264
275
|
*
|
|
265
276
|
* @remarks
|
|
266
277
|
* - Array & vector fields are statically sized (using
|
|
267
|
-
* {@link
|
|
278
|
+
* {@link Field.len})
|
|
268
279
|
* - Pointers are emitted as single-value pointers (where this distinction
|
|
269
280
|
* exist), i.e. even if they're pointing to multiple values, there's no
|
|
270
281
|
* explicit length encoded/available
|
|
@@ -274,7 +285,7 @@ export interface StructField extends TypeInfo {
|
|
|
274
285
|
*
|
|
275
286
|
* @defaultValue "scalar"
|
|
276
287
|
*/
|
|
277
|
-
tag?:
|
|
288
|
+
tag?: FieldTag;
|
|
278
289
|
/**
|
|
279
290
|
* Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
|
|
280
291
|
* value is interpreted as another type name in the {@link TypeColl}.
|
|
@@ -297,7 +308,7 @@ export interface StructField extends TypeInfo {
|
|
|
297
308
|
*/
|
|
298
309
|
sentinel?: number;
|
|
299
310
|
/**
|
|
300
|
-
* Array or vector length (see {@link
|
|
311
|
+
* Array or vector length (see {@link Field.tag})
|
|
301
312
|
*/
|
|
302
313
|
len?: number;
|
|
303
314
|
/**
|
|
@@ -306,11 +317,29 @@ export interface StructField extends TypeInfo {
|
|
|
306
317
|
*/
|
|
307
318
|
default?: number;
|
|
308
319
|
/**
|
|
309
|
-
* If defined and > 0, the field will be considered for padding purposes
|
|
310
|
-
* 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!
|
|
311
323
|
*/
|
|
312
324
|
pad?: number;
|
|
313
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
|
+
}
|
|
314
343
|
export interface Enum extends TopLevelType {
|
|
315
344
|
type: "enum";
|
|
316
345
|
/**
|
|
@@ -340,22 +369,42 @@ export interface EnumValue {
|
|
|
340
369
|
*/
|
|
341
370
|
doc?: string;
|
|
342
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
|
+
}
|
|
343
386
|
export interface CodeGenOptsBase {
|
|
344
387
|
/**
|
|
345
388
|
* Optional string to be injected before generated type defs (but after
|
|
346
389
|
* codegen's own prelude, if any)
|
|
347
390
|
*/
|
|
348
|
-
pre
|
|
391
|
+
pre?: string;
|
|
349
392
|
/**
|
|
350
393
|
* Optional string to be injected after generated type defs (but before
|
|
351
394
|
* codegen's own epilogue, if any)
|
|
352
395
|
*/
|
|
353
|
-
post
|
|
396
|
+
post?: string;
|
|
354
397
|
}
|
|
355
398
|
/**
|
|
356
399
|
* Global/shared code generator options.
|
|
357
400
|
*/
|
|
358
401
|
export interface CodeGenOpts extends CodeGenOptsBase {
|
|
402
|
+
/**
|
|
403
|
+
* WASM target specification.
|
|
404
|
+
*
|
|
405
|
+
* @defaultValue {@link WASM32}
|
|
406
|
+
*/
|
|
407
|
+
target: WasmTarget;
|
|
359
408
|
/**
|
|
360
409
|
* Identifier how strings are stored on WASM side, e.g. in Zig string
|
|
361
410
|
* literals are slices (8 bytes), in C just plain pointers (4 bytes).
|
|
@@ -409,13 +458,21 @@ export interface ICodeGen {
|
|
|
409
458
|
* Codegen for struct types.
|
|
410
459
|
*/
|
|
411
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;
|
|
412
469
|
}
|
|
413
470
|
/**
|
|
414
|
-
*
|
|
471
|
+
* WASM32 target spec
|
|
415
472
|
*/
|
|
416
|
-
export declare const
|
|
473
|
+
export declare const WASM32: WasmTarget;
|
|
417
474
|
/**
|
|
418
|
-
*
|
|
475
|
+
* WASM64 target spec
|
|
419
476
|
*/
|
|
420
|
-
export declare const
|
|
477
|
+
export declare const WASM64: WasmTarget;
|
|
421
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/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
|