@thi.ng/wasm-api 0.10.0 → 0.11.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/bridge.js CHANGED
@@ -2,10 +2,10 @@ import { __decorate } from "tslib";
2
2
  import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
3
3
  import { defError } from "@thi.ng/errors/deferror";
4
4
  import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
5
- import { U16, U32, U64HL, U8 } from "@thi.ng/hex";
5
+ import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
6
6
  import { ConsoleLogger } from "@thi.ng/logger/console";
7
7
  import { EVENT_MEMORY_CHANGED, } from "./api.js";
8
- const B32 = BigInt(32);
8
+ export const Panic = defError(() => "Panic");
9
9
  export const OutOfMemoryError = defError(() => "Out of memory");
10
10
  /**
11
11
  * The main interop API bridge between the JS host environment and a WebAssembly
@@ -34,18 +34,18 @@ let WasmBridge = class WasmBridge {
34
34
  this.api = {
35
35
  printI8: logN,
36
36
  printU8: logN,
37
- printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
38
37
  printI16: logN,
39
38
  printU16: logN,
40
- printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
41
39
  printI32: logN,
42
40
  printU32: (x) => this.logger.debug(x >>> 0),
43
- printU32Hex: (x) => this.logger.debug(`0x${U32(x)}`),
44
- _printI64: (hi, lo) => this.logger.debug((BigInt(hi) << B32) | BigInt(lo)),
45
- _printU64: (hi, lo) => this.logger.debug((BigInt(hi >>> 0) << B32) | BigInt(lo >>> 0)),
46
- _printU64Hex: (hi, lo) => this.logger.debug(`0x${U64HL(hi, lo)}`),
41
+ printI64: (x) => this.logger.debug(x),
42
+ printU64: (x) => this.logger.debug(x),
47
43
  printF32: logN,
48
44
  printF64: logN,
45
+ printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
46
+ printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
47
+ printU32Hex: (x) => this.logger.debug(`0x${U32(x)}`),
48
+ printU64Hex: (x) => this.logger.debug(`0x${U64BIG(x)}`),
49
49
  _printI8Array: logA(this.getI8Array.bind(this)),
50
50
  _printU8Array: logA(this.getU8Array.bind(this)),
51
51
  _printI16Array: logA(this.getI16Array.bind(this)),
@@ -61,6 +61,11 @@ let WasmBridge = class WasmBridge {
61
61
  debug: () => {
62
62
  debugger;
63
63
  },
64
+ _panic: (addr, len) => {
65
+ throw new Panic(this.getString(addr, len));
66
+ },
67
+ timer: () => performance.now(),
68
+ epoch: () => BigInt(Date.now()),
64
69
  };
65
70
  }
66
71
  /**
package/cli.js CHANGED
@@ -1,14 +1,16 @@
1
- import { flag, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
1
+ import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
2
  import { isArray, isPlainObject } from "@thi.ng/checks";
3
3
  import { illegalArgs } from "@thi.ng/errors";
4
- import { readJSON, writeText } from "@thi.ng/file-io";
4
+ import { mutIn } from "@thi.ng/paths/mut-in";
5
+ import { readJSON, readText, writeText } from "@thi.ng/file-io";
5
6
  import { ConsoleLogger } from "@thi.ng/logger";
6
7
  import { resolve } from "path";
7
8
  import { generateTypes } from "./codegen.js";
9
+ import { C11 } from "./codegen/c11.js";
8
10
  import { TYPESCRIPT } from "./codegen/typescript.js";
9
- import { isWasmPrim, isWasmString } from "./codegen/utils.js";
11
+ import { isPadding, isWasmPrim, isWasmString } from "./codegen/utils.js";
10
12
  import { ZIG } from "./codegen/zig.js";
11
- const GENERATORS = { ts: TYPESCRIPT, zig: ZIG };
13
+ const GENERATORS = { c11: C11, ts: TYPESCRIPT, zig: ZIG };
12
14
  const argOpts = {
13
15
  config: string({
14
16
  alias: "c",
@@ -27,6 +29,11 @@ const argOpts = {
27
29
  delim: ",",
28
30
  }),
29
31
  out: strings({ alias: "o", hint: "FILE", desc: "output file path" }),
32
+ string: oneOf(["slice", "ptr"], {
33
+ alias: "s",
34
+ hint: "TYPE",
35
+ desc: "Force string type implementation",
36
+ }),
30
37
  };
31
38
  export const INSTALL_DIR = resolve(`${process.argv[2]}/..`);
32
39
  export const PKG = readJSON(`${INSTALL_DIR}/package.json`);
@@ -72,7 +79,10 @@ const validateTypeRefs = (coll) => {
72
79
  if (spec.type !== "struct")
73
80
  continue;
74
81
  for (let f of spec.fields) {
75
- if (!(isWasmPrim(f.type) || isWasmString(f.type) || coll[f.type])) {
82
+ if (!(isPadding(f) ||
83
+ isWasmPrim(f.type) ||
84
+ isWasmString(f.type) ||
85
+ coll[f.type])) {
76
86
  invalidSpec(spec.__path, `structfield ${spec.name}.${f.name} of unknown type: ${f.type}`);
77
87
  }
78
88
  }
@@ -127,12 +137,23 @@ try {
127
137
  }
128
138
  const ctx = {
129
139
  logger: new ConsoleLogger("wasm-api", opts.debug ? "DEBUG" : "INFO"),
130
- config: {},
140
+ config: { global: {} },
131
141
  opts,
132
142
  };
133
143
  if (opts.config) {
134
144
  ctx.config = readJSON(resolve(opts.config), ctx.logger);
145
+ for (let id in ctx.config) {
146
+ const conf = ctx.config[id];
147
+ if (conf.pre && conf.pre[0] === "@") {
148
+ conf.pre = readText(conf.pre.substring(1), ctx.logger);
149
+ }
150
+ if (conf.post && conf.post[0] === "@") {
151
+ conf.post = readText(conf.post.substring(1), ctx.logger);
152
+ }
153
+ }
135
154
  }
155
+ opts.debug && mutIn(ctx, ["config", "global", "debug"], true);
156
+ opts.string && mutIn(ctx, ["config", "global", "stringType"], opts.string);
136
157
  generateOutputs(ctx, parseTypeSpecs(ctx, rest));
137
158
  }
138
159
  catch (e) {
package/codegen/c.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { ICodeGen } from "../api.js";
2
+ /**
3
+ * Zig code generator options.
4
+ */
5
+ export interface COpts {
6
+ /**
7
+ * If true, generates various struct & struct field analysis functions
8
+ * (sizes, alignment, offsets etc.).
9
+ *
10
+ * @defaultValue false
11
+ */
12
+ debug: boolean;
13
+ /**
14
+ * Optional prelude
15
+ */
16
+ pre: string;
17
+ /**
18
+ * Optional postfix (inserted after the generated code)
19
+ */
20
+ post: string;
21
+ }
22
+ /**
23
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
24
+ * (see its docs for further usage).
25
+ *
26
+ * @remarks
27
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
28
+ * given to {@link generateTypes}.
29
+ *
30
+ * @param opts
31
+ */
32
+ export declare const C11: (opts?: Partial<COpts>) => ICodeGen;
33
+ //# sourceMappingURL=c.d.ts.map
package/codegen/c.js ADDED
@@ -0,0 +1,100 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ import { isPadding, isStringSlice, prefixLines, withIndentation, } 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 C11 = (opts = {}) => {
14
+ const { debug } = { debug: false, ...opts };
15
+ const INDENT = " ";
16
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
17
+ const gen = {
18
+ pre: (opts) => `#pragma once
19
+ #include <stddef.h>
20
+ #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
21
+ post: () => opts.post || "",
22
+ doc: (doc, acc) => {
23
+ acc.push(prefixLines("// ", doc));
24
+ },
25
+ enum: (e, _, acc) => {
26
+ const lines = [];
27
+ lines.push(`enum {`);
28
+ for (let v of e.values) {
29
+ let line;
30
+ if (!isString(v)) {
31
+ v.doc && gen.doc(v.doc, lines);
32
+ line = `${e.name}_${v.name}`;
33
+ if (v.value != null)
34
+ line += ` = ${v.value}`;
35
+ }
36
+ else {
37
+ line = v;
38
+ }
39
+ lines.push(line + ",");
40
+ }
41
+ lines.push("};", "");
42
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
43
+ },
44
+ struct: (struct, _, acc, opts) => {
45
+ const name = struct.name;
46
+ const res = [];
47
+ res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
48
+ const ftypes = {};
49
+ let padID = 0;
50
+ for (let f of struct.fields) {
51
+ // autolabel explicit padding fields
52
+ if (isPadding(f)) {
53
+ res.push(`__pad${padID++}: [${f.pad}]u8,`);
54
+ continue;
55
+ }
56
+ f.doc && gen.doc(f.doc, res);
57
+ let ftype = f.type === "string"
58
+ ? isStringSlice(opts.stringType)
59
+ ? f.const !== false
60
+ ? "[]const u8"
61
+ : "[]u8"
62
+ : f.const !== false
63
+ ? "[*:0]const u8"
64
+ : "[*:0]u8"
65
+ : f.type;
66
+ switch (f.tag) {
67
+ case "array":
68
+ case "vec":
69
+ ftype = `[${f.len}]${ftype}`;
70
+ break;
71
+ case "slice":
72
+ ftype = `[]${f.const ? "const " : ""}${ftype}`;
73
+ break;
74
+ case "ptr":
75
+ ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
76
+ break;
77
+ case "scalar":
78
+ default:
79
+ }
80
+ ftypes[f.name] = ftype;
81
+ res.push(`${f.name}: ${ftype},`);
82
+ }
83
+ res.push("};");
84
+ if (debug) {
85
+ res.push("");
86
+ const fn = (fname, body) => res.push(`size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
87
+ fn("align", `alignof(${name})`);
88
+ fn("size", `sizeof(${name})`);
89
+ for (let f of struct.fields) {
90
+ fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
91
+ fn(f.name + "_offset", `offsetOf(${name}, "${f.name}")`);
92
+ fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
93
+ }
94
+ }
95
+ res.push("");
96
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
97
+ },
98
+ };
99
+ return gen;
100
+ };
@@ -0,0 +1,22 @@
1
+ import type { CodeGenOptsBase, ICodeGen } from "../api.js";
2
+ /**
3
+ * Zig code generator options.
4
+ */
5
+ export interface C11Opts extends CodeGenOptsBase {
6
+ /**
7
+ * Optional name prefix for generated types, e.g. `WASM_`.
8
+ */
9
+ typePrefix: string;
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 C11: (opts?: Partial<C11Opts>) => ICodeGen;
22
+ //# sourceMappingURL=c11.d.ts.map
package/codegen/c11.js ADDED
@@ -0,0 +1,125 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ import { unsupported } from "@thi.ng/errors/unsupported";
3
+ import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
4
+ const PRIM_ALIASES = {
5
+ i8: "int8_t",
6
+ u8: "uint8_t",
7
+ i16: "int16_t",
8
+ u16: "uint16_t",
9
+ i32: "int32_t",
10
+ u32: "uint32_t",
11
+ i64: "int64_t",
12
+ u64: "uint64_t",
13
+ f32: "float",
14
+ f64: "double",
15
+ };
16
+ /**
17
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
18
+ * (see its docs for further usage).
19
+ *
20
+ * @remarks
21
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
22
+ * given to {@link generateTypes}.
23
+ *
24
+ * @param opts
25
+ */
26
+ export const C11 = (opts = {}) => {
27
+ const { typePrefix } = {
28
+ typePrefix: "",
29
+ ...opts,
30
+ };
31
+ const INDENT = " ";
32
+ const SCOPES = [/\{$/, /^\}[ A-Za-z0-9_]*[;,]?$/];
33
+ const gen = {
34
+ pre: (opts) => `#pragma once
35
+ ${opts.debug ? "\n#include <stdalign.h>" : ""}
36
+ #include <stddef.h>
37
+ #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
38
+ post: () => opts.post || "",
39
+ doc: (doc, acc) => {
40
+ acc.push(prefixLines("// ", doc));
41
+ },
42
+ enum: (e, _, acc, opts) => {
43
+ if (!(e.tag === "i32" || e.tag === "u32")) {
44
+ unsupported(`enum ${e.name} must be a i32/u32 in C, but got '${e.tag}'`);
45
+ }
46
+ const name = typePrefix + e.name;
47
+ const lines = [];
48
+ lines.push(`typedef enum {`);
49
+ for (let v of e.values) {
50
+ let line;
51
+ if (!isString(v)) {
52
+ v.doc && gen.doc(v.doc, lines);
53
+ line = enumName(opts, v.name);
54
+ if (v.value != null)
55
+ line += ` = ${v.value}`;
56
+ }
57
+ else {
58
+ line = enumName(opts, v);
59
+ }
60
+ lines.push(line + ",");
61
+ }
62
+ lines.push(`} ${name};`, "");
63
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
64
+ },
65
+ struct: (struct, coll, acc, opts) => {
66
+ const name = typePrefix + struct.name;
67
+ const res = [];
68
+ res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
69
+ const ftypes = {};
70
+ let padID = 0;
71
+ for (let f of struct.fields) {
72
+ // autolabel explicit padding fields
73
+ if (isPadding(f)) {
74
+ res.push(`uint8_t __pad${padID++}[${f.pad}];`);
75
+ continue;
76
+ }
77
+ f.doc && gen.doc(f.doc, res);
78
+ const fconst = f.const ? "const " : "";
79
+ let ftype = f.type === "string"
80
+ ? isStringSlice(opts.stringType)
81
+ ? __slice("char", fconst)
82
+ : `${f.const !== false ? "const " : ""}char*`
83
+ : PRIM_ALIASES[f.type] || f.type;
84
+ if (coll[ftype])
85
+ ftype = typePrefix + ftype;
86
+ switch (f.tag) {
87
+ case "array":
88
+ case "vec":
89
+ res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
90
+ ftype = `${ftype}[${f.len}]`;
91
+ break;
92
+ case "slice":
93
+ ftype = __slice(ftype, fconst);
94
+ res.push(`${ftype} ${f.name};`);
95
+ break;
96
+ case "ptr":
97
+ ftype = `${fconst}${ftype}*`;
98
+ res.push(`${ftype} ${f.name};`);
99
+ break;
100
+ case "scalar":
101
+ default:
102
+ res.push(`${ftype} ${f.name};`);
103
+ }
104
+ ftypes[f.name] = ftype;
105
+ }
106
+ res.push("};");
107
+ if (opts.debug) {
108
+ const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
109
+ fn("align", `alignof(${name})`);
110
+ fn("size", `sizeof(${name})`);
111
+ for (let f of struct.fields) {
112
+ if (isPadding(f))
113
+ continue;
114
+ fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
115
+ fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
116
+ fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
117
+ }
118
+ }
119
+ res.push("");
120
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
121
+ },
122
+ };
123
+ return gen;
124
+ };
125
+ const __slice = (type, $const) => `struct { ${$const}${type} *ptr; size_t len; }`;
@@ -1,24 +1,14 @@
1
- import { ICodeGen } from "../api.js";
1
+ import { CodeGenOptsBase, ICodeGen } from "../api.js";
2
2
  /**
3
3
  * TypeScript code generator options.
4
4
  */
5
- export interface TSOpts {
5
+ export interface TSOpts extends CodeGenOptsBase {
6
6
  /**
7
7
  * Indentation string
8
8
  *
9
9
  * @defaultValue "\t"
10
10
  */
11
11
  indent: string;
12
- /**
13
- * If true (default), forces uppercase enums
14
- *
15
- * @defaultValue true
16
- */
17
- uppercaseEnums: boolean;
18
- /**
19
- * Same as {@link CodeGenOpts.stringType}.
20
- */
21
- stringType: "slice" | "ptr";
22
12
  }
23
13
  /**
24
14
  * TypeScript code generator. Call with options and then pass to