@thi.ng/wasm-api 0.5.0 → 0.6.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 +35 -1
- package/README.md +169 -16
- package/api.d.ts +143 -5
- package/api.js +9 -1
- package/bridge.d.ts +41 -7
- package/bridge.js +44 -8
- package/codegen/typescript.d.ts +22 -0
- package/codegen/typescript.js +146 -0
- package/codegen/utils.d.ts +5 -0
- package/codegen/utils.js +7 -0
- package/codegen/zig.d.ts +22 -0
- package/codegen/zig.js +75 -0
- package/codegen.d.ts +16 -0
- package/codegen.js +116 -0
- package/include/wasmapi.h +60 -0
- package/{zig/core.zig → include/wasmapi.zig} +45 -32
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +27 -7
- package/dev/custom.zig +0 -12
- package/dev/fieldinfo.zig +0 -135
- package/dev/hello.zig +0 -14
- package/dev/zig-cache/o/0fd683610fe16c12563bf410950c8193/builtin.zig +0 -39
- package/test/custom.zig +0 -16
- package/test/zig-cache/o/117d44467ded6ce3864cde3c180ef73c/builtin.zig +0 -39
package/bridge.js
CHANGED
|
@@ -28,7 +28,7 @@ export class WasmBridge {
|
|
|
28
28
|
this.utf8Encoder = new TextEncoder();
|
|
29
29
|
const logN = (x) => this.logger.debug(x);
|
|
30
30
|
const logA = (method) => (addr, len) => this.logger.debug(method(addr, len).join(", "));
|
|
31
|
-
this.
|
|
31
|
+
this.api = {
|
|
32
32
|
printI8: logN,
|
|
33
33
|
printU8: logN,
|
|
34
34
|
printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
|
|
@@ -136,7 +136,7 @@ export class WasmBridge {
|
|
|
136
136
|
* bridge.getImports();
|
|
137
137
|
* {
|
|
138
138
|
* // imports defined by the core API of the bridge itself
|
|
139
|
-
*
|
|
139
|
+
* wasmapi: { ... },
|
|
140
140
|
* // imports defined by the CustomAPI module
|
|
141
141
|
* custom: { ... }
|
|
142
142
|
* }
|
|
@@ -151,7 +151,7 @@ export class WasmBridge {
|
|
|
151
151
|
*/
|
|
152
152
|
getImports() {
|
|
153
153
|
if (!this.imports) {
|
|
154
|
-
this.imports = {
|
|
154
|
+
this.imports = { wasmapi: this.api };
|
|
155
155
|
for (let id in this.modules) {
|
|
156
156
|
if (this.imports[id] !== undefined) {
|
|
157
157
|
illegalArgs(`attempt to redeclare API module ${id}`);
|
|
@@ -176,24 +176,36 @@ export class WasmBridge {
|
|
|
176
176
|
* Attempts to allocate `numBytes` using the exported WASM core API function
|
|
177
177
|
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
178
178
|
* start address of the new memory block. If unsuccessful, throws an
|
|
179
|
-
* {@link OutOfMemoryError}.
|
|
179
|
+
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
180
|
+
* be zero-filled.
|
|
180
181
|
*
|
|
181
182
|
* @remarks
|
|
182
183
|
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
183
184
|
*
|
|
184
185
|
* @param numBytes
|
|
186
|
+
* @param clear
|
|
185
187
|
*/
|
|
186
|
-
allocate(numBytes) {
|
|
188
|
+
allocate(numBytes, clear = false) {
|
|
187
189
|
const addr = this.exports._wasm_allocate(numBytes);
|
|
188
190
|
if (!addr)
|
|
189
191
|
throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
|
|
190
192
|
this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)}`);
|
|
191
193
|
this.ensureMemory();
|
|
194
|
+
clear && this.u8.fill(0, addr, addr + numBytes);
|
|
192
195
|
return addr;
|
|
193
196
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Frees a previous allocated memory region using the exported WASM core API
|
|
199
|
+
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
200
|
+
* `numBytes` value must be the same as previously given to
|
|
201
|
+
* {@link WasmBridge.allocate}.
|
|
202
|
+
*
|
|
203
|
+
* @param addr
|
|
204
|
+
* @param numBytes
|
|
205
|
+
*/
|
|
206
|
+
free(addr, numBytes) {
|
|
207
|
+
this.logger.debug(`freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
208
|
+
this.exports._wasm_free(addr, numBytes);
|
|
197
209
|
}
|
|
198
210
|
getI8(addr) {
|
|
199
211
|
return this.i8[addr];
|
|
@@ -343,9 +355,33 @@ export class WasmBridge {
|
|
|
343
355
|
this.f64.set(buf, addr >> 3);
|
|
344
356
|
return this;
|
|
345
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Reads UTF-8 encoded string from given address and optional byte length.
|
|
360
|
+
* The default length is 0, which will be interpreted as a zero-terminated
|
|
361
|
+
* string. Returns string.
|
|
362
|
+
*
|
|
363
|
+
* @param addr
|
|
364
|
+
* @param len
|
|
365
|
+
*/
|
|
346
366
|
getString(addr, len = 0) {
|
|
367
|
+
this.ensureMemory();
|
|
347
368
|
return this.utf8Decoder.decode(this.u8.subarray(addr, len > 0 ? addr + len : this.u8.indexOf(0, addr)));
|
|
348
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Encodes given string as UTF-8 and writes it to WASM memory starting at
|
|
372
|
+
* `addr`. By default the string will be zero-terminated and only `maxBytes`
|
|
373
|
+
* will be written. Returns the number of bytes written.
|
|
374
|
+
*
|
|
375
|
+
* @remarks
|
|
376
|
+
* An error will be thrown if the encoded string doesn't fully fit into the
|
|
377
|
+
* designated memory region (also note that there might need to be space for
|
|
378
|
+
* the additional sentinel/termination byte).
|
|
379
|
+
*
|
|
380
|
+
* @param str
|
|
381
|
+
* @param addr
|
|
382
|
+
* @param maxBytes
|
|
383
|
+
* @param terminate
|
|
384
|
+
*/
|
|
349
385
|
setString(str, addr, maxBytes, terminate = true) {
|
|
350
386
|
maxBytes = Math.min(maxBytes, this.u8.length - addr);
|
|
351
387
|
const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ICodeGen } from "../api.js";
|
|
2
|
+
export interface TSOpts {
|
|
3
|
+
/**
|
|
4
|
+
* Indentation string
|
|
5
|
+
*
|
|
6
|
+
* @defaultValue "\t"
|
|
7
|
+
*/
|
|
8
|
+
indent: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* TypeScript code generator. Call with options and then pass to
|
|
12
|
+
* {@link generateTypes} (see its docs for further usage).
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* This codegen generates interface and enum definitions for a {@link TypeColl}
|
|
16
|
+
* given to {@link generateTypes}. For structs it will also generate memory
|
|
17
|
+
* mapped wrappers with fully typed accessors.
|
|
18
|
+
*
|
|
19
|
+
* @param opts
|
|
20
|
+
*/
|
|
21
|
+
export declare const TYPESCRIPT: (opts?: Partial<TSOpts>) => ICodeGen;
|
|
22
|
+
//# sourceMappingURL=typescript.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
|
|
2
|
+
import { isString } from "@thi.ng/checks/is-string";
|
|
3
|
+
import { PKG_NAME, USIZE, } from "../api.js";
|
|
4
|
+
import { isBigNumeric, isNumeric, isPrim, prefixLines } from "./utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* TypeScript code generator. Call with options and then pass to
|
|
7
|
+
* {@link generateTypes} (see its docs for further usage).
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* This codegen generates interface and enum definitions for a {@link TypeColl}
|
|
11
|
+
* given to {@link generateTypes}. For structs it will also generate memory
|
|
12
|
+
* mapped wrappers with fully typed accessors.
|
|
13
|
+
*
|
|
14
|
+
* @param opts
|
|
15
|
+
*/
|
|
16
|
+
export const TYPESCRIPT = (opts) => {
|
|
17
|
+
const { indent } = { indent: "\t", ...opts };
|
|
18
|
+
const I = indent;
|
|
19
|
+
const I2 = I + I;
|
|
20
|
+
const I3 = I2 + I;
|
|
21
|
+
const gen = {
|
|
22
|
+
pre: `import type { WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";`,
|
|
23
|
+
doc: (doc, indent, acc) => {
|
|
24
|
+
if (doc.indexOf("\n") !== -1) {
|
|
25
|
+
acc.push(indent + "/**", prefixLines(indent + " * ", doc), indent + " */");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
acc.push(`${indent}/** ${doc} */`);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
enum: (type, _, acc) => {
|
|
32
|
+
const e = type;
|
|
33
|
+
acc.push(`export enum ${e.name} {`);
|
|
34
|
+
for (let v of e.values) {
|
|
35
|
+
var line = indent;
|
|
36
|
+
if (!isString(v)) {
|
|
37
|
+
v.doc && gen.doc(v.doc, indent, acc);
|
|
38
|
+
line += v.name;
|
|
39
|
+
if (v.value != null)
|
|
40
|
+
line += ` = ${v.value}`;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
line += v;
|
|
44
|
+
}
|
|
45
|
+
acc.push(line + ",");
|
|
46
|
+
}
|
|
47
|
+
acc.push("}\n");
|
|
48
|
+
return acc;
|
|
49
|
+
},
|
|
50
|
+
struct: (type, types, acc) => {
|
|
51
|
+
const struct = type;
|
|
52
|
+
const returnTypes = {};
|
|
53
|
+
// interface definition
|
|
54
|
+
acc.push(`export interface ${struct.name} extends WasmTypeBase {`);
|
|
55
|
+
for (let f of struct.fields) {
|
|
56
|
+
f.doc && gen.doc(f.doc, indent, acc);
|
|
57
|
+
let line = `${indent}${f.name}: `;
|
|
58
|
+
let rtype = "";
|
|
59
|
+
if (f.tag == "array" || f.tag == "slice" || f.tag === "vec") {
|
|
60
|
+
rtype = isNumeric(f.type)
|
|
61
|
+
? TYPEDARRAY_CTORS[f.type].name
|
|
62
|
+
: isBigNumeric(f.type)
|
|
63
|
+
? BIGINT_ARRAY_CTORS[f.type].name
|
|
64
|
+
: f.type + "[]";
|
|
65
|
+
}
|
|
66
|
+
else if (!f.tag || f.tag === "scalar" || f.tag === "ptr") {
|
|
67
|
+
rtype = isBigNumeric(f.type)
|
|
68
|
+
? "bigint"
|
|
69
|
+
: isNumeric(f.type)
|
|
70
|
+
? "number"
|
|
71
|
+
: f.type;
|
|
72
|
+
}
|
|
73
|
+
returnTypes[f.name] = rtype;
|
|
74
|
+
acc.push(line + rtype + ";");
|
|
75
|
+
}
|
|
76
|
+
acc.push("}\n");
|
|
77
|
+
// type implementation
|
|
78
|
+
acc.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `${I}get align() { return ${struct.__align}; },`, `${I}get size() { return ${struct.__size}; },`, `${I}instance: (base) => ({`, `${I2}get __base() { return base; },`, `${I2}get __bytes() { return mem.u8.subarray(base, base + ${struct.__size}); },`);
|
|
79
|
+
for (let f of struct.fields) {
|
|
80
|
+
const offset = f.__offset || 0;
|
|
81
|
+
acc.push(`${I2}get ${f.name}(): ${returnTypes[f.name]} {`);
|
|
82
|
+
const prim = isPrim(f.type);
|
|
83
|
+
if (f.tag === "ptr") {
|
|
84
|
+
acc.push(prim
|
|
85
|
+
? `${I3}return mem.${f.type}[${__ptrShift(offset, f.type)}];`
|
|
86
|
+
: `${I3}return $${f.type}.instance(${__ptr(offset)});`);
|
|
87
|
+
}
|
|
88
|
+
else if (f.tag === "slice") {
|
|
89
|
+
acc.push(`${I3}const len = ${__ptr(offset + 4)};`, prim
|
|
90
|
+
? `${I3}const addr = ${__ptrShift(offset, f.type)};
|
|
91
|
+
${I3}return mem.${f.type}.subarray(addr, addr + len);`
|
|
92
|
+
: `${I3}const addr = ${__ptr(offset)};\n${__mapArray(struct, f, I3)}`);
|
|
93
|
+
}
|
|
94
|
+
else if (f.tag === "array" || f.tag === "vec") {
|
|
95
|
+
acc.push(prim
|
|
96
|
+
? `${I3}const addr = ${__addrShift(offset, f.type)};
|
|
97
|
+
${I3}return mem.${f.type}.subarray(addr, addr + ${f.len});`
|
|
98
|
+
: `${I3}const addr = ${__addr(offset)};\n${__mapArray(struct, f, I3, f.len)}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
let setter;
|
|
102
|
+
if (prim) {
|
|
103
|
+
const addr = __mem(f.type, f.__offset);
|
|
104
|
+
acc.push(`${I3}return ${addr};`);
|
|
105
|
+
setter = `${addr} = x`;
|
|
106
|
+
}
|
|
107
|
+
else if (types[f.type].type === "enum") {
|
|
108
|
+
const tag = types[f.type].tag;
|
|
109
|
+
const addr = __mem(tag, f.__offset);
|
|
110
|
+
acc.push(`${I3}return ${addr};`);
|
|
111
|
+
setter = `${addr} = x`;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
acc.push(`${I3}return $${f.type}(mem).instance(${__addr(offset)});`);
|
|
115
|
+
setter = `mem.u8.set(x.__bytes, ${__addr(offset)})`;
|
|
116
|
+
}
|
|
117
|
+
// close getter
|
|
118
|
+
acc.push(`${I2}},`);
|
|
119
|
+
// setter
|
|
120
|
+
acc.push(`${I2}set ${f.name}(x: ${returnTypes[f.name]}) {`, `${I3}${setter};`);
|
|
121
|
+
}
|
|
122
|
+
// close field accessor
|
|
123
|
+
acc.push(`${I2}},`);
|
|
124
|
+
}
|
|
125
|
+
acc.push(`${I}})\n});\n`);
|
|
126
|
+
return acc;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
return gen;
|
|
130
|
+
};
|
|
131
|
+
/** @internal */
|
|
132
|
+
const __shift = (type) => BIT_SHIFTS[type];
|
|
133
|
+
/** @internal */
|
|
134
|
+
const __addr = (offset) => (offset > 0 ? `(base + ${offset})` : "base");
|
|
135
|
+
/** @internal */
|
|
136
|
+
const __addrShift = (offset, shift) => __addr(offset) + " >>> " + __shift(shift);
|
|
137
|
+
/** @internal */
|
|
138
|
+
const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
|
|
139
|
+
/** @internal */
|
|
140
|
+
const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
|
|
141
|
+
const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
|
|
142
|
+
/** @internal */
|
|
143
|
+
const __mapArray = (struct, f, indent, len = "len") => prefixLines(indent, `const inst = $${f.type}(mem);
|
|
144
|
+
const slice: ${f.type}[] = [];
|
|
145
|
+
for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${struct.__size}));
|
|
146
|
+
return slice;`);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const isNumeric: (x: string) => boolean;
|
|
2
|
+
export declare const isBigNumeric: (x: string) => boolean;
|
|
3
|
+
export declare const isPrim: (x: string) => boolean;
|
|
4
|
+
export declare const prefixLines: (prefix: string, str: string) => string;
|
|
5
|
+
//# sourceMappingURL=utils.d.ts.map
|
package/codegen/utils.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const isNumeric = (x) => /^([iu](8|16|32))|(f(32|64))$/.test(x);
|
|
2
|
+
export const isBigNumeric = (x) => /^[iu]64$/.test(x);
|
|
3
|
+
export const isPrim = (x) => isNumeric(x) || isBigNumeric(x);
|
|
4
|
+
export const prefixLines = (prefix, str) => str
|
|
5
|
+
.split("\n")
|
|
6
|
+
.map((line) => prefix + line)
|
|
7
|
+
.join("\n");
|
package/codegen/zig.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ICodeGen } from "../api.js";
|
|
2
|
+
export interface ZigOpts {
|
|
3
|
+
/**
|
|
4
|
+
* If true, generates various struct & struct field analysis functions
|
|
5
|
+
* (sizes, alignment, offsets etc.).
|
|
6
|
+
*
|
|
7
|
+
* @defaultValue false
|
|
8
|
+
*/
|
|
9
|
+
debug: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Zig code generator. Call with options and then pass to {@link generateTypes}
|
|
13
|
+
* (see its docs for further usage).
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* This codegen generates struct and enum definitions for a {@link TypeColl}
|
|
17
|
+
* given to {@link generateTypes}.
|
|
18
|
+
*
|
|
19
|
+
* @param opts
|
|
20
|
+
*/
|
|
21
|
+
export declare const ZIG: (opts?: Partial<ZigOpts>) => ICodeGen;
|
|
22
|
+
//# sourceMappingURL=zig.d.ts.map
|
package/codegen/zig.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { isString } from "@thi.ng/checks/is-string";
|
|
2
|
+
import { prefixLines } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Zig code generator. Call with options and then pass to {@link generateTypes}
|
|
5
|
+
* (see its docs for further usage).
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This codegen generates struct and enum definitions for a {@link TypeColl}
|
|
9
|
+
* given to {@link generateTypes}.
|
|
10
|
+
*
|
|
11
|
+
* @param opts
|
|
12
|
+
*/
|
|
13
|
+
export const ZIG = (opts) => {
|
|
14
|
+
const { debug } = { debug: false, ...opts };
|
|
15
|
+
const gen = {
|
|
16
|
+
doc: (doc, indent, acc, topLevel = false) => {
|
|
17
|
+
acc.push(prefixLines(topLevel ? "//! " : indent + "/// ", doc));
|
|
18
|
+
},
|
|
19
|
+
enum: (e, _, acc) => {
|
|
20
|
+
acc.push(`pub const ${e.name} = enum(${e.tag}) {`);
|
|
21
|
+
for (let v of e.values) {
|
|
22
|
+
let line = ` `;
|
|
23
|
+
if (!isString(v)) {
|
|
24
|
+
v.doc && gen.doc(v.doc, " ", acc);
|
|
25
|
+
line += v.name;
|
|
26
|
+
if (v.value != null)
|
|
27
|
+
line += ` = ${v.value}`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
line += v;
|
|
31
|
+
}
|
|
32
|
+
acc.push(line + ",");
|
|
33
|
+
}
|
|
34
|
+
acc.push("};\n");
|
|
35
|
+
},
|
|
36
|
+
struct: (struct, _, acc) => {
|
|
37
|
+
const name = struct.name;
|
|
38
|
+
acc.push(`pub const ${name} = struct {`);
|
|
39
|
+
const ftypes = {};
|
|
40
|
+
for (let f of struct.fields) {
|
|
41
|
+
f.doc && gen.doc(f.doc, " ", acc);
|
|
42
|
+
var ftype;
|
|
43
|
+
switch (f.tag) {
|
|
44
|
+
case "array":
|
|
45
|
+
ftype = `[${f.len}]${f.type}`;
|
|
46
|
+
break;
|
|
47
|
+
case "slice":
|
|
48
|
+
ftype = `[]${f.type}`;
|
|
49
|
+
break;
|
|
50
|
+
case "vec":
|
|
51
|
+
ftype = `@Vector(${f.len}, ${f.type})`;
|
|
52
|
+
break;
|
|
53
|
+
case "ptr":
|
|
54
|
+
ftype = `*${f.len ? `[${f.len}]` : ""}${f.type}`;
|
|
55
|
+
break;
|
|
56
|
+
case "scalar":
|
|
57
|
+
default:
|
|
58
|
+
ftype = f.type;
|
|
59
|
+
}
|
|
60
|
+
ftypes[f.name] = ftype;
|
|
61
|
+
acc.push(` ${f.name}: ${ftype},`);
|
|
62
|
+
}
|
|
63
|
+
acc.push("};\n");
|
|
64
|
+
if (!debug)
|
|
65
|
+
return;
|
|
66
|
+
const fn = (fname, body) => `export fn ${name}_${fname}() usize { return ${body}; }`;
|
|
67
|
+
acc.push(fn("align", `@alignOf(${name})`), fn("size", `@sizeOf(${name})`));
|
|
68
|
+
for (let f of struct.fields) {
|
|
69
|
+
acc.push(fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`), fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`), fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`));
|
|
70
|
+
}
|
|
71
|
+
acc.push("");
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
return gen;
|
|
75
|
+
};
|
package/codegen.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ICodeGen, TypeColl } from "./api.js";
|
|
2
|
+
export interface CodeGenOpts {
|
|
3
|
+
/**
|
|
4
|
+
* Optional string to be injected before generated type defs (but after
|
|
5
|
+
* codegen's own prelude, if any)
|
|
6
|
+
*/
|
|
7
|
+
pre: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional string to be injected after generated type defs (but before
|
|
10
|
+
* codegen's own epilogue, if any)
|
|
11
|
+
*/
|
|
12
|
+
post: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const prepareTypes: (types: TypeColl) => void;
|
|
15
|
+
export declare const generateTypes: (types: TypeColl, codegen: ICodeGen, opts?: Partial<CodeGenOpts>) => string;
|
|
16
|
+
//# sourceMappingURL=codegen.d.ts.map
|
package/codegen.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { SIZEOF } from "@thi.ng/api/typedarray";
|
|
2
|
+
import { align } from "@thi.ng/binary/align";
|
|
3
|
+
import { ceilPow2 } from "@thi.ng/binary/pow";
|
|
4
|
+
import { compareByKey } from "@thi.ng/compare/keys";
|
|
5
|
+
import { compareNumDesc } from "@thi.ng/compare/numeric";
|
|
6
|
+
import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
|
|
7
|
+
import { PKG_NAME, USIZE_SIZE, } from "./api.js";
|
|
8
|
+
import { isNumeric } from "./codegen/utils.js";
|
|
9
|
+
const sizeOf = defmulti((x) => x.type, {}, {
|
|
10
|
+
[DEFAULT]: (field, types) => {
|
|
11
|
+
if (field.__size)
|
|
12
|
+
return field.__size;
|
|
13
|
+
let size = 0;
|
|
14
|
+
if (field.tag === "ptr") {
|
|
15
|
+
size = USIZE_SIZE;
|
|
16
|
+
}
|
|
17
|
+
else if (field.tag === "slice") {
|
|
18
|
+
size = USIZE_SIZE * 2;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
size = isNumeric(field.type)
|
|
22
|
+
? SIZEOF[field.type]
|
|
23
|
+
: sizeOf(types[field.type], types);
|
|
24
|
+
if (field.tag == "array" || field.tag === "vec") {
|
|
25
|
+
size *= field.len;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return (field.__size = align(size, field.__align));
|
|
29
|
+
},
|
|
30
|
+
enum: (type) => {
|
|
31
|
+
if (type.__size)
|
|
32
|
+
return type.__size;
|
|
33
|
+
return (type.__size = SIZEOF[type.tag]);
|
|
34
|
+
},
|
|
35
|
+
struct: (type, types) => {
|
|
36
|
+
if (type.__size)
|
|
37
|
+
return type.__size;
|
|
38
|
+
const struct = type;
|
|
39
|
+
let size = 0;
|
|
40
|
+
for (let f of struct.fields) {
|
|
41
|
+
size = align(size, f.__align);
|
|
42
|
+
f.__offset = size;
|
|
43
|
+
size += sizeOf(f, types);
|
|
44
|
+
}
|
|
45
|
+
return (type.__size = align(size, type.__align));
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const alignOf = defmulti((x) => x.type, {}, {
|
|
49
|
+
[DEFAULT]: (field, types) => {
|
|
50
|
+
if (field.__align)
|
|
51
|
+
return field.__align;
|
|
52
|
+
let align = isNumeric(field.type)
|
|
53
|
+
? SIZEOF[field.type]
|
|
54
|
+
: alignOf(types[field.type], types);
|
|
55
|
+
if (field.tag === "vec") {
|
|
56
|
+
align *= ceilPow2(field.len);
|
|
57
|
+
}
|
|
58
|
+
field.__align = align;
|
|
59
|
+
return align;
|
|
60
|
+
},
|
|
61
|
+
enum: (e) => {
|
|
62
|
+
return (e.__align = SIZEOF[e.tag]);
|
|
63
|
+
},
|
|
64
|
+
struct: (type, types) => {
|
|
65
|
+
const struct = type;
|
|
66
|
+
let maxAlign = 0;
|
|
67
|
+
for (let f of struct.fields) {
|
|
68
|
+
maxAlign = Math.max(maxAlign, alignOf(f, types));
|
|
69
|
+
}
|
|
70
|
+
return (type.__align = maxAlign);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const prepareType = defmulti((x) => x.type, {}, {
|
|
74
|
+
[DEFAULT]: (x, types) => {
|
|
75
|
+
if (x.__align && x.__size)
|
|
76
|
+
return;
|
|
77
|
+
alignOf(x, types);
|
|
78
|
+
sizeOf(x, types);
|
|
79
|
+
},
|
|
80
|
+
struct: (x, types) => {
|
|
81
|
+
if (x.__align && x.__size)
|
|
82
|
+
return;
|
|
83
|
+
const struct = x;
|
|
84
|
+
alignOf(struct, types);
|
|
85
|
+
if (struct.auto) {
|
|
86
|
+
struct.fields.sort(compareByKey("__align", compareNumDesc));
|
|
87
|
+
}
|
|
88
|
+
for (let f of struct.fields) {
|
|
89
|
+
if (types[f.type]) {
|
|
90
|
+
prepareType(types[f.type], types);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
sizeOf(struct, types);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
export const prepareTypes = (types) => {
|
|
97
|
+
for (let id in types) {
|
|
98
|
+
prepareType(types[id], types);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
export const generateTypes = (types, codegen, opts = {}) => {
|
|
102
|
+
prepareTypes(types);
|
|
103
|
+
const res = [];
|
|
104
|
+
codegen.doc(`Generated by ${PKG_NAME} at ${new Date().toISOString()} - DO NOT EDIT!`, "", res, true);
|
|
105
|
+
res.push("");
|
|
106
|
+
codegen.pre && res.push(codegen.pre, "");
|
|
107
|
+
opts.pre && res.push(opts.pre, "");
|
|
108
|
+
for (let id in types) {
|
|
109
|
+
const type = types[id];
|
|
110
|
+
type.doc && codegen.doc(type.doc, "", res);
|
|
111
|
+
codegen[type.type](type, types, res);
|
|
112
|
+
}
|
|
113
|
+
opts.post && res.push("", opts.post);
|
|
114
|
+
codegen.post && res.push("", codegen.post);
|
|
115
|
+
return res.join("\n");
|
|
116
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __cplusplus
|
|
4
|
+
extern "C" {
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
|
|
10
|
+
#define WASM_IMPORT(MODULE, TYPE, NAME, PREFIX) \
|
|
11
|
+
extern __attribute__((import_module(MODULE), import_name(#NAME))) \
|
|
12
|
+
TYPE PREFIX##NAME
|
|
13
|
+
#define WASM_KEEP __attribute__((used))
|
|
14
|
+
|
|
15
|
+
// Generate stubs only if explicitly disabled by defining this symbol
|
|
16
|
+
#ifdef WASMAPI_NO_MALLOC
|
|
17
|
+
size_t WASM_KEEP _wasm_allocate(size_t num_bytes) { return 0; }
|
|
18
|
+
void WASM_KEEP _wasm_free(size_t addr) {}
|
|
19
|
+
#else
|
|
20
|
+
#include <stdlib.h>
|
|
21
|
+
size_t WASM_KEEP _wasm_allocate(size_t numBytes) {
|
|
22
|
+
return (size_t)malloc(numBytes);
|
|
23
|
+
}
|
|
24
|
+
void WASM_KEEP _wasm_free(size_t addr) { free((void*)addr); }
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
WASM_IMPORT("wasmapi", void, printI8, wasm_)(int8_t x);
|
|
28
|
+
WASM_IMPORT("wasmapi", void, printU8, wasm_)(uint8_t x);
|
|
29
|
+
WASM_IMPORT("wasmapi", void, printU8Hex, wasm_)(uint8_t x);
|
|
30
|
+
WASM_IMPORT("wasmapi", void, printI16, wasm_)(int16_t x);
|
|
31
|
+
WASM_IMPORT("wasmapi", void, printU16, wasm_)(uint16_t x);
|
|
32
|
+
WASM_IMPORT("wasmapi", void, printU16Hex, wasm_)(uint16_t x);
|
|
33
|
+
WASM_IMPORT("wasmapi", void, printI32, wasm_)(int32_t x);
|
|
34
|
+
WASM_IMPORT("wasmapi", void, printU32, wasm_)(uint32_t x);
|
|
35
|
+
WASM_IMPORT("wasmapi", void, printU32Hex, wasm_)(uint32_t x);
|
|
36
|
+
WASM_IMPORT("wasmapi", void, printI64, wasm_)(int64_t x);
|
|
37
|
+
WASM_IMPORT("wasmapi", void, printU64, wasm_)(uint64_t x);
|
|
38
|
+
WASM_IMPORT("wasmapi", void, printU64Hex, wasm_)(uint64_t x);
|
|
39
|
+
WASM_IMPORT("wasmapi", void, printF32, wasm_)(float x);
|
|
40
|
+
WASM_IMPORT("wasmapi", void, printF64, wasm_)(double x);
|
|
41
|
+
|
|
42
|
+
WASM_IMPORT("wasmapi", void, _printI8Array, wasm)(void* addr, size_t len);
|
|
43
|
+
WASM_IMPORT("wasmapi", void, _printU8Array, wasm)(void* addr, size_t len);
|
|
44
|
+
WASM_IMPORT("wasmapi", void, _printI16Array, wasm)(void* addr, size_t len);
|
|
45
|
+
WASM_IMPORT("wasmapi", void, _printU16Array, wasm)(void* addr, size_t len);
|
|
46
|
+
WASM_IMPORT("wasmapi", void, _printI32Array, wasm)(void* addr, size_t len);
|
|
47
|
+
WASM_IMPORT("wasmapi", void, _printU32Array, wasm)(void* addr, size_t len);
|
|
48
|
+
WASM_IMPORT("wasmapi", void, _printI64Array, wasm)(void* addr, size_t len);
|
|
49
|
+
WASM_IMPORT("wasmapi", void, _printU64Array, wasm)(void* addr, size_t len);
|
|
50
|
+
WASM_IMPORT("wasmapi", void, _printF32Array, wasm)(void* addr, size_t len);
|
|
51
|
+
WASM_IMPORT("wasmapi", void, _printF64Array, wasm)(void* addr, size_t len);
|
|
52
|
+
|
|
53
|
+
WASM_IMPORT("wasmapi", void, _printStr0, wasm)(void* addr);
|
|
54
|
+
WASM_IMPORT("wasmapi", void, _printStr, wasm)(void* addr, size_t len);
|
|
55
|
+
|
|
56
|
+
void wasm_printPtr(void* ptr) { wasm_printU32Hex((size_t)ptr); }
|
|
57
|
+
|
|
58
|
+
#ifdef __cplusplus
|
|
59
|
+
}
|
|
60
|
+
#endif
|