@thi.ng/wasm-api 0.9.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/CHANGELOG.md +60 -1
- package/README.md +169 -127
- package/api.d.ts +102 -23
- package/api.js +1 -0
- package/bridge.d.ts +35 -6
- package/bridge.js +55 -15
- package/cli.js +27 -6
- package/codegen/c.d.ts +33 -0
- package/codegen/c.js +100 -0
- package/codegen/c11.d.ts +22 -0
- package/codegen/c11.js +125 -0
- package/codegen/typescript.d.ts +2 -12
- package/codegen/typescript.js +120 -84
- package/codegen/utils.d.ts +48 -1
- package/codegen/utils.js +55 -0
- package/codegen/zig.d.ts +2 -9
- package/codegen/zig.js +54 -28
- package/codegen.d.ts +1 -23
- package/codegen.js +31 -8
- package/doc/assets/main.js +52 -0
- package/doc/assets/search.js +1 -0
- package/include/wasmapi.h +38 -23
- package/include/wasmapi.zig +69 -65
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +37 -19
- package/pointer.d.ts +23 -0
- package/pointer.js +27 -0
- package/string.d.ts +97 -0
- package/string.js +145 -0
package/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { BigType, FloatType, Fn
|
|
1
|
+
import type { BigType, FloatType, Fn } from "@thi.ng/api";
|
|
2
2
|
import type { WasmBridge } from "./bridge.js";
|
|
3
3
|
export declare const PKG_NAME = "@thi.ng/wasm-api";
|
|
4
|
+
export declare const EVENT_MEMORY_CHANGED = "memory-changed";
|
|
4
5
|
export declare type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
|
|
5
6
|
/**
|
|
6
7
|
* Common interface for WASM/JS child APIs which will be used in combination
|
|
@@ -27,9 +28,9 @@ export interface IWasmAPI<T extends WasmExports = WasmExports> {
|
|
|
27
28
|
getImports(): WebAssembly.ModuleImports;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
* Base interface of exports declared by the WASM module. At the very least,
|
|
31
|
-
* module needs to export its memory and the functions defined in
|
|
32
|
-
* interface.
|
|
31
|
+
* Base interface of exports declared by the WASM module. At the very least, a
|
|
32
|
+
* compatible module needs to export its memory and the functions defined in
|
|
33
|
+
* this interface.
|
|
33
34
|
*
|
|
34
35
|
* @remarks
|
|
35
36
|
* This interface is supposed to be extended with the concrete exports defined
|
|
@@ -45,21 +46,34 @@ export interface WasmExports {
|
|
|
45
46
|
*/
|
|
46
47
|
memory: WebAssembly.Memory;
|
|
47
48
|
/**
|
|
48
|
-
* Implementation specific memory allocation function
|
|
49
|
-
*
|
|
50
|
-
* unsuccessful.
|
|
49
|
+
* Implementation specific WASM memory allocation function. If successful
|
|
50
|
+
* returns address of new memory block, or zero if unsuccessful.
|
|
51
51
|
*
|
|
52
52
|
* @remarks
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
53
|
+
* #### Zig
|
|
54
|
+
*
|
|
55
|
+
* Using the supplied Zig bindings (see `/include/wasmapi.zig`), it's the
|
|
56
|
+
* user's responsibility to define a public `WASM_ALLOCATOR` in the root
|
|
57
|
+
* source file to enable allocations, e.g. using the
|
|
58
|
+
* [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
|
|
59
|
+
* (which also automatically handles growing the WASM memory). However, as
|
|
60
|
+
* mentioned, the underlying mechanism is purposefully left to the actual
|
|
61
|
+
* WASM-side implementation. If no allocator is defined this function
|
|
62
|
+
* returns zero, which in turn will cause {@link WasmBridge.allocate} to
|
|
63
|
+
* throw an error.
|
|
64
|
+
*
|
|
65
|
+
* #### C/C++
|
|
66
|
+
*
|
|
67
|
+
* Using the supplied C bindings (see `/include/wasmapi.h`), it's the user's
|
|
68
|
+
* responsibility to enable allocation support by defining the
|
|
69
|
+
* `WASMAPI_MALLOC` symbol (and compiling the WASM module with a malloc
|
|
70
|
+
* implementation).
|
|
58
71
|
*/
|
|
59
72
|
_wasm_allocate(numBytes: number): number;
|
|
60
73
|
/**
|
|
61
74
|
* Implementation specific function to free a previously allocated chunk of
|
|
62
|
-
* of WASM memory (allocated via {@link WasmExports._wasm_allocate}
|
|
75
|
+
* of WASM memory (allocated via {@link WasmExports._wasm_allocate}, also
|
|
76
|
+
* see remarks for that function).
|
|
63
77
|
*
|
|
64
78
|
* @param addr
|
|
65
79
|
* @param numBytes
|
|
@@ -118,9 +132,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
|
|
|
118
132
|
printI32: Fn<number, void>;
|
|
119
133
|
printU32: Fn<number, void>;
|
|
120
134
|
printU32Hex: Fn<number, void>;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
printI64: Fn<bigint, void>;
|
|
136
|
+
printU64: Fn<bigint, void>;
|
|
137
|
+
printU64Hex: Fn<bigint, void>;
|
|
124
138
|
printF32: Fn<number, void>;
|
|
125
139
|
printF64: Fn<number, void>;
|
|
126
140
|
_printI8Array: (addr: number, len: number) => void;
|
|
@@ -136,6 +150,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
|
|
|
136
150
|
_printStr0: (addr: number) => void;
|
|
137
151
|
_printStr: (addr: number, len: number) => void;
|
|
138
152
|
debug: () => void;
|
|
153
|
+
_panic: (addr: number, len: number) => void;
|
|
154
|
+
timer: () => number;
|
|
155
|
+
epoch: () => bigint;
|
|
139
156
|
}
|
|
140
157
|
export interface WasmTypeBase {
|
|
141
158
|
/**
|
|
@@ -203,11 +220,18 @@ export interface Struct extends TopLevelType {
|
|
|
203
220
|
/**
|
|
204
221
|
* If true, struct fields will be re-ordered in descending order based on
|
|
205
222
|
* their {@link TypeInfo.__align} size. This might result in overall smaller
|
|
206
|
-
* structs due to minimizing inter-field padding
|
|
223
|
+
* structs due to minimizing implicit inter-field padding caused by
|
|
224
|
+
* alignment requirements. **If this option is enabled, then the struct MUST
|
|
225
|
+
* NOT contain any padding fields!**
|
|
207
226
|
*
|
|
208
227
|
* @defaultValue false
|
|
209
228
|
*/
|
|
210
229
|
auto?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Optional qualifier for the kind of struct to be emitted (codegen specific
|
|
232
|
+
* interpretation, currently only used by {@link ZIG}).
|
|
233
|
+
*/
|
|
234
|
+
tag?: "extern" | "packed";
|
|
211
235
|
}
|
|
212
236
|
export interface StructField extends TypeInfo {
|
|
213
237
|
/**
|
|
@@ -246,6 +270,11 @@ export interface StructField extends TypeInfo {
|
|
|
246
270
|
* TODO `opaque` currently unsupported.
|
|
247
271
|
*/
|
|
248
272
|
type: WasmPrim | "string" | "opaque" | string;
|
|
273
|
+
/**
|
|
274
|
+
* Const qualifier (default is true for `string`, false for all other
|
|
275
|
+
* types). Only used for pointers or slices.
|
|
276
|
+
*/
|
|
277
|
+
const?: boolean;
|
|
249
278
|
/**
|
|
250
279
|
* TODO currently unsupported & ignored!
|
|
251
280
|
*/
|
|
@@ -258,11 +287,19 @@ export interface StructField extends TypeInfo {
|
|
|
258
287
|
* TODO currently unsupported & ignored!
|
|
259
288
|
*/
|
|
260
289
|
default?: any;
|
|
290
|
+
/**
|
|
291
|
+
* If defined and > 0, the field will be considered for padding purposes only and
|
|
292
|
+
* the value provided is the number of bytes used.
|
|
293
|
+
*/
|
|
294
|
+
pad?: number;
|
|
261
295
|
}
|
|
262
296
|
export interface Enum extends TopLevelType {
|
|
263
297
|
type: "enum";
|
|
264
298
|
/**
|
|
265
|
-
* No i64/u64 support, due to Typescript not supporting bigint enum values
|
|
299
|
+
* No i64/u64 support, due to Typescript not supporting bigint enum values.
|
|
300
|
+
* For C compatibility only i32 or u32 is allowed.
|
|
301
|
+
*
|
|
302
|
+
* @defaultValue "i32"
|
|
266
303
|
*/
|
|
267
304
|
tag: Exclude<WasmPrim32, FloatType>;
|
|
268
305
|
/**
|
|
@@ -285,27 +322,69 @@ export interface EnumValue {
|
|
|
285
322
|
*/
|
|
286
323
|
doc?: string;
|
|
287
324
|
}
|
|
325
|
+
export interface CodeGenOptsBase {
|
|
326
|
+
/**
|
|
327
|
+
* Optional string to be injected before generated type defs (but after
|
|
328
|
+
* codegen's own prelude, if any)
|
|
329
|
+
*/
|
|
330
|
+
pre: string;
|
|
331
|
+
/**
|
|
332
|
+
* Optional string to be injected after generated type defs (but before
|
|
333
|
+
* codegen's own epilogue, if any)
|
|
334
|
+
*/
|
|
335
|
+
post: string;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Global/shared code generator options.
|
|
339
|
+
*/
|
|
340
|
+
export interface CodeGenOpts extends CodeGenOptsBase {
|
|
341
|
+
/**
|
|
342
|
+
* Identifier how strings are stored on WASM side, e.g. in Zig string
|
|
343
|
+
* literals are slices (8 bytes), in C just plain pointers (4 bytes).
|
|
344
|
+
*
|
|
345
|
+
* @defaultValue "slice"
|
|
346
|
+
*/
|
|
347
|
+
stringType: "slice" | "ptr";
|
|
348
|
+
/**
|
|
349
|
+
* If true (default), forces uppercase enum identifiers
|
|
350
|
+
*
|
|
351
|
+
* @defaultValue true
|
|
352
|
+
*/
|
|
353
|
+
uppercaseEnums: boolean;
|
|
354
|
+
/**
|
|
355
|
+
* Unless set to false, the generated output will be prefixed with a header
|
|
356
|
+
* line comment of generator meta data
|
|
357
|
+
*/
|
|
358
|
+
header: boolean;
|
|
359
|
+
/**
|
|
360
|
+
* If true, codegens MAY generate various additional struct & struct field
|
|
361
|
+
* analysis functions (sizes, alignment, offsets etc.).
|
|
362
|
+
*
|
|
363
|
+
* @defaultValue false
|
|
364
|
+
*/
|
|
365
|
+
debug: boolean;
|
|
366
|
+
}
|
|
288
367
|
export interface ICodeGen {
|
|
289
368
|
/**
|
|
290
369
|
* Optional prelude source, to be prepended before any generated type defs.
|
|
291
370
|
*/
|
|
292
|
-
pre?: string
|
|
371
|
+
pre?: Fn<CodeGenOpts, string>;
|
|
293
372
|
/**
|
|
294
373
|
* Optional source code to be appended after any generated type defs.
|
|
295
374
|
*/
|
|
296
|
-
post?: string
|
|
375
|
+
post?: Fn<CodeGenOpts, string>;
|
|
297
376
|
/**
|
|
298
377
|
* Docstring codegen
|
|
299
378
|
*/
|
|
300
|
-
doc: (doc: string,
|
|
379
|
+
doc: (doc: string, acc: string[], topLevel?: boolean) => void;
|
|
301
380
|
/**
|
|
302
381
|
* Codegen for enum types.
|
|
303
382
|
*/
|
|
304
|
-
enum: (type: Enum, types: TypeColl, acc: string[]) => void;
|
|
383
|
+
enum: (type: Enum, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
305
384
|
/**
|
|
306
385
|
* Codegen for struct types.
|
|
307
386
|
*/
|
|
308
|
-
struct: (type: Struct, types: TypeColl, acc: string[]) => void;
|
|
387
|
+
struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
|
|
309
388
|
}
|
|
310
389
|
/**
|
|
311
390
|
* WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
|
package/api.js
CHANGED
package/bridge.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import type { NumericArray } from "@thi.ng/api";
|
|
2
|
+
import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
|
|
3
3
|
import type { ILogger } from "@thi.ng/logger";
|
|
4
|
-
import
|
|
4
|
+
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
|
|
5
|
+
export declare const Panic: {
|
|
6
|
+
new (msg?: string | undefined): {
|
|
7
|
+
name: string;
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function | undefined): void;
|
|
12
|
+
prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
|
|
13
|
+
stackTraceLimit: number;
|
|
14
|
+
};
|
|
5
15
|
export declare const OutOfMemoryError: {
|
|
6
16
|
new (msg?: string | undefined): {
|
|
7
17
|
name: string;
|
|
@@ -28,7 +38,7 @@ export declare const OutOfMemoryError: {
|
|
|
28
38
|
* 64bit integers are handled via JS `BigInt` and hence require the host env to
|
|
29
39
|
* support it. No polyfill is provided.
|
|
30
40
|
*/
|
|
31
|
-
export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess {
|
|
41
|
+
export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess, INotify {
|
|
32
42
|
modules: Record<string, IWasmAPI<T>>;
|
|
33
43
|
logger: ILogger;
|
|
34
44
|
i8: Int8Array;
|
|
@@ -66,14 +76,23 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
66
76
|
* then initializes all declared bridge child API modules. Returns false if
|
|
67
77
|
* any of the module initializations failed.
|
|
68
78
|
*
|
|
79
|
+
* @remarks
|
|
80
|
+
* Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
|
|
81
|
+
* AFTER all child API modules have been initialized).
|
|
82
|
+
*
|
|
69
83
|
* @param exports
|
|
70
84
|
*/
|
|
71
85
|
init(exports: T): Promise<boolean>;
|
|
72
86
|
/**
|
|
73
|
-
* Called automatically. Initializes and/or updates
|
|
74
|
-
* memory views (e.g. after growing the WASM memory
|
|
87
|
+
* Called automatically during initialization. Initializes and/or updates
|
|
88
|
+
* the various typed WASM memory views (e.g. after growing the WASM memory
|
|
89
|
+
* and the previous buffer becoming detached). Unless `notify` is false,
|
|
90
|
+
* the {@link EVENT_MEMORY_CHANGED} event will be emitted if the memory
|
|
91
|
+
* views had to be updated.
|
|
92
|
+
*
|
|
93
|
+
* @param notify
|
|
75
94
|
*/
|
|
76
|
-
ensureMemory(): void;
|
|
95
|
+
ensureMemory(notify?: boolean): void;
|
|
77
96
|
/**
|
|
78
97
|
* Required use for WASM module instantiation to provide JS imports to the
|
|
79
98
|
* module. Returns an object of all WASM imports declared by the bridge core
|
|
@@ -137,6 +156,10 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
137
156
|
* `numBytes` value must be the same as previously given to
|
|
138
157
|
* {@link WasmBridge.allocate}.
|
|
139
158
|
*
|
|
159
|
+
* @remarks
|
|
160
|
+
* This function always succeeds, regardless of presence of an active
|
|
161
|
+
* allocator on the WASM side or validity of given arguments.
|
|
162
|
+
*
|
|
140
163
|
* @param addr
|
|
141
164
|
* @param numBytes
|
|
142
165
|
*/
|
|
@@ -208,5 +231,11 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
208
231
|
*/
|
|
209
232
|
setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
|
|
210
233
|
getElementById(addr: number, len?: number): HTMLElement;
|
|
234
|
+
/** {@inheritDoc @thi.ng/api#INotify.addListener} */
|
|
235
|
+
addListener(id: string, fn: Listener, scope?: any): boolean;
|
|
236
|
+
/** {@inheritDoc @thi.ng/api#INotify.removeListener} */
|
|
237
|
+
removeListener(id: string, fn: Listener, scope?: any): boolean;
|
|
238
|
+
/** {@inheritDoc @thi.ng/api#INotify.notify} */
|
|
239
|
+
notify(event: Event): void;
|
|
211
240
|
}
|
|
212
241
|
//# sourceMappingURL=bridge.d.ts.map
|
package/bridge.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
|
|
1
3
|
import { defError } from "@thi.ng/errors/deferror";
|
|
2
4
|
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
3
|
-
import { U16, U32,
|
|
5
|
+
import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
|
|
4
6
|
import { ConsoleLogger } from "@thi.ng/logger/console";
|
|
5
|
-
|
|
7
|
+
import { EVENT_MEMORY_CHANGED, } from "./api.js";
|
|
8
|
+
export const Panic = defError(() => "Panic");
|
|
6
9
|
export const OutOfMemoryError = defError(() => "Out of memory");
|
|
7
10
|
/**
|
|
8
11
|
* The main interop API bridge between the JS host environment and a WebAssembly
|
|
@@ -20,7 +23,7 @@ export const OutOfMemoryError = defError(() => "Out of memory");
|
|
|
20
23
|
* 64bit integers are handled via JS `BigInt` and hence require the host env to
|
|
21
24
|
* support it. No polyfill is provided.
|
|
22
25
|
*/
|
|
23
|
-
|
|
26
|
+
let WasmBridge = class WasmBridge {
|
|
24
27
|
constructor(modules = {}, logger = new ConsoleLogger("wasm")) {
|
|
25
28
|
this.modules = modules;
|
|
26
29
|
this.logger = logger;
|
|
@@ -31,18 +34,18 @@ export class WasmBridge {
|
|
|
31
34
|
this.api = {
|
|
32
35
|
printI8: logN,
|
|
33
36
|
printU8: logN,
|
|
34
|
-
printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
|
|
35
37
|
printI16: logN,
|
|
36
38
|
printU16: logN,
|
|
37
|
-
printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
|
|
38
39
|
printI32: logN,
|
|
39
40
|
printU32: (x) => this.logger.debug(x >>> 0),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
_printU64: (hi, lo) => this.logger.debug((BigInt(hi >>> 0) << B32) | BigInt(lo >>> 0)),
|
|
43
|
-
_printU64Hex: (hi, lo) => this.logger.debug(`0x${U64HL(hi, lo)}`),
|
|
41
|
+
printI64: (x) => this.logger.debug(x),
|
|
42
|
+
printU64: (x) => this.logger.debug(x),
|
|
44
43
|
printF32: logN,
|
|
45
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)}`),
|
|
46
49
|
_printI8Array: logA(this.getI8Array.bind(this)),
|
|
47
50
|
_printU8Array: logA(this.getU8Array.bind(this)),
|
|
48
51
|
_printI16Array: logA(this.getI16Array.bind(this)),
|
|
@@ -58,6 +61,11 @@ export class WasmBridge {
|
|
|
58
61
|
debug: () => {
|
|
59
62
|
debugger;
|
|
60
63
|
},
|
|
64
|
+
_panic: (addr, len) => {
|
|
65
|
+
throw new Panic(this.getString(addr, len));
|
|
66
|
+
},
|
|
67
|
+
timer: () => performance.now(),
|
|
68
|
+
epoch: () => BigInt(Date.now()),
|
|
61
69
|
};
|
|
62
70
|
}
|
|
63
71
|
/**
|
|
@@ -86,24 +94,34 @@ export class WasmBridge {
|
|
|
86
94
|
* then initializes all declared bridge child API modules. Returns false if
|
|
87
95
|
* any of the module initializations failed.
|
|
88
96
|
*
|
|
97
|
+
* @remarks
|
|
98
|
+
* Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
|
|
99
|
+
* AFTER all child API modules have been initialized).
|
|
100
|
+
*
|
|
89
101
|
* @param exports
|
|
90
102
|
*/
|
|
91
103
|
async init(exports) {
|
|
92
104
|
this.exports = exports;
|
|
93
|
-
this.ensureMemory();
|
|
105
|
+
this.ensureMemory(false);
|
|
94
106
|
for (let id in this.modules) {
|
|
95
107
|
this.logger.debug(`initializing API module: ${id}`);
|
|
96
108
|
const status = await this.modules[id].init(this);
|
|
97
109
|
if (!status)
|
|
98
110
|
return false;
|
|
99
111
|
}
|
|
112
|
+
this.notify({ id: EVENT_MEMORY_CHANGED, value: this.exports.memory });
|
|
100
113
|
return true;
|
|
101
114
|
}
|
|
102
115
|
/**
|
|
103
|
-
* Called automatically. Initializes and/or updates
|
|
104
|
-
* memory views (e.g. after growing the WASM memory
|
|
116
|
+
* Called automatically during initialization. Initializes and/or updates
|
|
117
|
+
* the various typed WASM memory views (e.g. after growing the WASM memory
|
|
118
|
+
* and the previous buffer becoming detached). Unless `notify` is false,
|
|
119
|
+
* the {@link EVENT_MEMORY_CHANGED} event will be emitted if the memory
|
|
120
|
+
* views had to be updated.
|
|
121
|
+
*
|
|
122
|
+
* @param notify
|
|
105
123
|
*/
|
|
106
|
-
ensureMemory() {
|
|
124
|
+
ensureMemory(notify = true) {
|
|
107
125
|
const buf = this.exports.memory.buffer;
|
|
108
126
|
if (this.u8 && this.u8.buffer === buf)
|
|
109
127
|
return;
|
|
@@ -117,6 +135,11 @@ export class WasmBridge {
|
|
|
117
135
|
this.u64 = new BigUint64Array(buf);
|
|
118
136
|
this.f32 = new Float32Array(buf);
|
|
119
137
|
this.f64 = new Float64Array(buf);
|
|
138
|
+
notify &&
|
|
139
|
+
this.notify({
|
|
140
|
+
id: EVENT_MEMORY_CHANGED,
|
|
141
|
+
value: this.exports.memory,
|
|
142
|
+
});
|
|
120
143
|
}
|
|
121
144
|
/**
|
|
122
145
|
* Required use for WASM module instantiation to provide JS imports to the
|
|
@@ -192,7 +215,7 @@ export class WasmBridge {
|
|
|
192
215
|
const addr = this.exports._wasm_allocate(numBytes);
|
|
193
216
|
if (!addr)
|
|
194
217
|
throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
|
|
195
|
-
this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)}`);
|
|
218
|
+
this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
196
219
|
this.ensureMemory();
|
|
197
220
|
clear && this.u8.fill(0, addr, addr + numBytes);
|
|
198
221
|
return addr;
|
|
@@ -203,6 +226,10 @@ export class WasmBridge {
|
|
|
203
226
|
* `numBytes` value must be the same as previously given to
|
|
204
227
|
* {@link WasmBridge.allocate}.
|
|
205
228
|
*
|
|
229
|
+
* @remarks
|
|
230
|
+
* This function always succeeds, regardless of presence of an active
|
|
231
|
+
* allocator on the WASM side or validity of given arguments.
|
|
232
|
+
*
|
|
206
233
|
* @param addr
|
|
207
234
|
* @param numBytes
|
|
208
235
|
*/
|
|
@@ -403,4 +430,17 @@ export class WasmBridge {
|
|
|
403
430
|
el == null && illegalArgs(`missing DOM element #${id}`);
|
|
404
431
|
return el;
|
|
405
432
|
}
|
|
406
|
-
}
|
|
433
|
+
/** {@inheritDoc @thi.ng/api#INotify.addListener} */
|
|
434
|
+
// @ts-ignore: mixin
|
|
435
|
+
addListener(id, fn, scope) { }
|
|
436
|
+
/** {@inheritDoc @thi.ng/api#INotify.removeListener} */
|
|
437
|
+
// @ts-ignore: mixin
|
|
438
|
+
removeListener(id, fn, scope) { }
|
|
439
|
+
/** {@inheritDoc @thi.ng/api#INotify.notify} */
|
|
440
|
+
// @ts-ignore: mixin
|
|
441
|
+
notify(event) { }
|
|
442
|
+
};
|
|
443
|
+
WasmBridge = __decorate([
|
|
444
|
+
INotifyMixin
|
|
445
|
+
], WasmBridge);
|
|
446
|
+
export { WasmBridge };
|
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 {
|
|
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 (!(
|
|
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
|
+
};
|
package/codegen/c11.d.ts
ADDED
|
@@ -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
|