@thi.ng/wasm-api 0.15.0 → 0.17.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 +39 -1
- package/api.d.ts +74 -29
- package/bridge.d.ts +3 -29
- package/bridge.js +2 -28
- package/cli.js +2 -2
- package/codegen/typescript.js +20 -3
- package/codegen/utils.d.ts +20 -11
- package/codegen/utils.js +36 -12
- package/codegen/zig.d.ts +6 -1
- package/codegen/zig.js +48 -42
- package/codegen.js +2 -2
- package/package.json +19 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2022-10-
|
|
3
|
+
- **Last updated**: 2022-10-29T20:34:04Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,44 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
## [0.17.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.17.0) (2022-10-29)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- update default value format & handling ([c7752fb](https://github.com/thi-ng/umbrella/commit/c7752fb))
|
|
17
|
+
- update Field.default to support lang-specific alts
|
|
18
|
+
- add defaultValue() helper
|
|
19
|
+
- update zig codegen
|
|
20
|
+
|
|
21
|
+
#### 🩹 Bug fixes
|
|
22
|
+
|
|
23
|
+
- fix idempotence checks for self-referential types ([2c3f670](https://github.com/thi-ng/umbrella/commit/2c3f670))
|
|
24
|
+
- update prepareType()
|
|
25
|
+
|
|
26
|
+
## [0.16.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.16.0) (2022-10-28)
|
|
27
|
+
|
|
28
|
+
#### 🚀 Features
|
|
29
|
+
|
|
30
|
+
- update TS docstring generator ([5a060b6](https://github.com/thi-ng/umbrella/commit/5a060b6))
|
|
31
|
+
- include native/WASM type in docstring (use Zig type sigs)
|
|
32
|
+
- update ensureLines() helper
|
|
33
|
+
- update test fixtures
|
|
34
|
+
- update IWasmMemoryAccess & impls ([bb8a3ca](https://github.com/thi-ng/umbrella/commit/bb8a3ca))
|
|
35
|
+
- add MemorySlice tuple to describe a memory region
|
|
36
|
+
- update allocate() & free() to use MemorySlice, migrate to IWasmMemoryAccess
|
|
37
|
+
- migrate growMemory() to IWasmMemoryAccess
|
|
38
|
+
- add MemorySlice to default imports in TS codegen
|
|
39
|
+
- update docstring & user code codegen config ([68694ba](https://github.com/thi-ng/umbrella/commit/68694ba))
|
|
40
|
+
- allow doc strings & user codes to be given as string array (lines)
|
|
41
|
+
- add InjectedBody type to support multiple injection sites
|
|
42
|
+
(e.g. TS interface declarations vs. wrapper impls)
|
|
43
|
+
- update TS & Zig codegens
|
|
44
|
+
- add internal ensureLines() helper
|
|
45
|
+
|
|
46
|
+
#### 🩹 Bug fixes
|
|
47
|
+
|
|
48
|
+
- fix ensureLines(), update tests & fixtures ([f8a4668](https://github.com/thi-ng/umbrella/commit/f8a4668))
|
|
49
|
+
|
|
12
50
|
## [0.15.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.15.0) (2022-10-26)
|
|
13
51
|
|
|
14
52
|
#### 🚀 Features
|
package/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf } from "@thi.ng/api";
|
|
1
|
+
import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf, NumOrString } from "@thi.ng/api";
|
|
2
2
|
import type { Pow2 } from "@thi.ng/binary";
|
|
3
3
|
import type { WasmBridge } from "./bridge.js";
|
|
4
4
|
export declare const PKG_NAME = "@thi.ng/wasm-api";
|
|
@@ -53,7 +53,7 @@ export interface WasmExports {
|
|
|
53
53
|
* @remarks
|
|
54
54
|
* #### Zig
|
|
55
55
|
*
|
|
56
|
-
* Using the supplied Zig bindings (see `/
|
|
56
|
+
* Using the supplied Zig bindings (see `/zig/wasmapi.zig`), it's the
|
|
57
57
|
* user's responsibility to define a public `WASM_ALLOCATOR` in the root
|
|
58
58
|
* source file to enable allocations, e.g. using the
|
|
59
59
|
* [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
|
|
@@ -81,6 +81,7 @@ export interface WasmExports {
|
|
|
81
81
|
*/
|
|
82
82
|
_wasm_free(addr: number, numBytes: number): void;
|
|
83
83
|
}
|
|
84
|
+
export declare type MemorySlice = [addr: number, len: number];
|
|
84
85
|
export interface IWasmMemoryAccess {
|
|
85
86
|
i8: Int8Array;
|
|
86
87
|
u8: Uint8Array;
|
|
@@ -97,6 +98,41 @@ export interface IWasmMemoryAccess {
|
|
|
97
98
|
* after growing the WASM memory and the previous buffer becoming detached).
|
|
98
99
|
*/
|
|
99
100
|
ensureMemory(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Attempts to grow the WASM memory by an additional `numPages` (64KB/page)
|
|
103
|
+
* and if successful updates all typed memory views to use the new
|
|
104
|
+
* underlying buffer.
|
|
105
|
+
*
|
|
106
|
+
* @param numPages
|
|
107
|
+
*/
|
|
108
|
+
growMemory(numPages: number): void;
|
|
109
|
+
/**
|
|
110
|
+
* Attempts to allocate `numBytes` using the exported WASM core API function
|
|
111
|
+
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
112
|
+
* start address of the new memory block. If unsuccessful, throws an
|
|
113
|
+
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
114
|
+
* be zero-filled.
|
|
115
|
+
*
|
|
116
|
+
* @remarks
|
|
117
|
+
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
118
|
+
*
|
|
119
|
+
* @param numBytes
|
|
120
|
+
* @param clear
|
|
121
|
+
*/
|
|
122
|
+
allocate(numBytes: number, clear?: boolean): MemorySlice;
|
|
123
|
+
/**
|
|
124
|
+
* Frees a previous allocated memory region using the exported WASM core API
|
|
125
|
+
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
126
|
+
* `numBytes` value must be the same as previously given to
|
|
127
|
+
* {@link IWasmMemoryAccess.allocate}.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* This function always succeeds, regardless of presence of an active
|
|
131
|
+
* allocator on the WASM side or validity of given arguments.
|
|
132
|
+
*
|
|
133
|
+
* @param slice
|
|
134
|
+
*/
|
|
135
|
+
free(slice: MemorySlice): void;
|
|
100
136
|
/**
|
|
101
137
|
* Reads UTF-8 encoded string from given address and optional byte length.
|
|
102
138
|
* The default length is 0, which will be interpreted as a zero-terminated
|
|
@@ -188,7 +224,7 @@ export declare type WasmPrim32 = Exclude<WasmPrim, BigType>;
|
|
|
188
224
|
export declare type ReadonlyWasmString = IDeref<string> & ILength & {
|
|
189
225
|
readonly addr: number;
|
|
190
226
|
};
|
|
191
|
-
export declare type TypeColl =
|
|
227
|
+
export declare type TypeColl = IObjectOf<TopLevelType>;
|
|
192
228
|
export interface TypeInfo {
|
|
193
229
|
/**
|
|
194
230
|
* Auto-computed size (in bytes)
|
|
@@ -217,19 +253,24 @@ export interface TopLevelType extends TypeInfo {
|
|
|
217
253
|
/**
|
|
218
254
|
* Optional (multi-line) docstring for this type
|
|
219
255
|
*/
|
|
220
|
-
doc?: string;
|
|
256
|
+
doc?: string | string[];
|
|
221
257
|
/**
|
|
222
258
|
* Type / kind
|
|
223
259
|
*/
|
|
224
260
|
type: "enum" | "struct" | "union";
|
|
225
261
|
/**
|
|
226
262
|
* Optional object of user provided source codes to be injected into the
|
|
227
|
-
* generated type
|
|
263
|
+
* generated type (after generated fields). Keys of this object are language
|
|
264
|
+
* IDs (`ts` for {@link TYPESCRIPT}, `zig` for {@link ZIG}).
|
|
228
265
|
*
|
|
229
266
|
* @remarks
|
|
230
|
-
* Currently only supported by the
|
|
267
|
+
* Currently only supported by the code gens mentioned, ignored otherwise.
|
|
231
268
|
*/
|
|
232
|
-
body?: IObjectOf<string>;
|
|
269
|
+
body?: IObjectOf<string | string[] | InjectedBody>;
|
|
270
|
+
}
|
|
271
|
+
export interface InjectedBody {
|
|
272
|
+
decl?: string | string[];
|
|
273
|
+
impl?: string | string[];
|
|
233
274
|
}
|
|
234
275
|
export interface Struct extends TopLevelType {
|
|
235
276
|
type: "struct";
|
|
@@ -259,6 +300,23 @@ export interface Struct extends TopLevelType {
|
|
|
259
300
|
*/
|
|
260
301
|
align?: AlignStrategy;
|
|
261
302
|
}
|
|
303
|
+
export interface Union extends TopLevelType {
|
|
304
|
+
type: "union";
|
|
305
|
+
/**
|
|
306
|
+
* Array of union fields.
|
|
307
|
+
*/
|
|
308
|
+
fields: Field[];
|
|
309
|
+
/**
|
|
310
|
+
* Optional qualifier for the kind of struct to be emitted (codegen specific
|
|
311
|
+
* interpretation, currently only used by {@link ZIG}).
|
|
312
|
+
*/
|
|
313
|
+
tag?: "extern" | "packed";
|
|
314
|
+
/**
|
|
315
|
+
* Optional user supplied {@link AlignStrategy}. By default uses
|
|
316
|
+
* {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
|
|
317
|
+
*/
|
|
318
|
+
align?: AlignStrategy;
|
|
319
|
+
}
|
|
262
320
|
export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
|
|
263
321
|
export interface Field extends TypeInfo {
|
|
264
322
|
/**
|
|
@@ -268,7 +326,7 @@ export interface Field extends TypeInfo {
|
|
|
268
326
|
/**
|
|
269
327
|
* Field docstring (can be multiline, will be formatted)
|
|
270
328
|
*/
|
|
271
|
-
doc?: string;
|
|
329
|
+
doc?: string | string[];
|
|
272
330
|
/**
|
|
273
331
|
* Field type tag/qualifier (note: `slice` & `vec` are only supported by Zig
|
|
274
332
|
* & TS).
|
|
@@ -312,10 +370,14 @@ export interface Field extends TypeInfo {
|
|
|
312
370
|
*/
|
|
313
371
|
len?: number;
|
|
314
372
|
/**
|
|
315
|
-
* Currently only supported for {@link ZIG}
|
|
316
|
-
*
|
|
373
|
+
* Currently only supported for {@link ZIG}, otherwise ignored!
|
|
374
|
+
*
|
|
375
|
+
* @remarks
|
|
376
|
+
* The object form allows for different default values per language (in
|
|
377
|
+
* theory). So if given as object, the keys refer to the lang ID and the
|
|
378
|
+
* values as the defaults for those languages.
|
|
317
379
|
*/
|
|
318
|
-
default?:
|
|
380
|
+
default?: NumOrString | IObjectOf<NumOrString>;
|
|
319
381
|
/**
|
|
320
382
|
* If defined and > 0, the field will be considered for padding purposes
|
|
321
383
|
* only and the value provided is the number of bytes used. All other config
|
|
@@ -323,23 +385,6 @@ export interface Field extends TypeInfo {
|
|
|
323
385
|
*/
|
|
324
386
|
pad?: number;
|
|
325
387
|
}
|
|
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
|
-
}
|
|
343
388
|
export interface Enum extends TopLevelType {
|
|
344
389
|
type: "enum";
|
|
345
390
|
/**
|
|
@@ -449,7 +494,7 @@ export interface ICodeGen {
|
|
|
449
494
|
/**
|
|
450
495
|
* Docstring codegen
|
|
451
496
|
*/
|
|
452
|
-
doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
|
|
497
|
+
doc: (doc: string | string[], acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
|
|
453
498
|
/**
|
|
454
499
|
* Codegen for enum types.
|
|
455
500
|
*/
|
package/bridge.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
|
|
3
3
|
import type { ILogger } from "@thi.ng/logger";
|
|
4
|
-
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
|
|
4
|
+
import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, MemorySlice, WasmExports } from "./api.js";
|
|
5
5
|
export declare const Panic: {
|
|
6
6
|
new (msg?: string | undefined): {
|
|
7
7
|
name: string;
|
|
@@ -136,34 +136,8 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
|
|
|
136
136
|
* @param numPages
|
|
137
137
|
*/
|
|
138
138
|
growMemory(numPages: number): void;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
142
|
-
* start address of the new memory block. If unsuccessful, throws an
|
|
143
|
-
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
144
|
-
* be zero-filled.
|
|
145
|
-
*
|
|
146
|
-
* @remarks
|
|
147
|
-
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
148
|
-
*
|
|
149
|
-
* @param numBytes
|
|
150
|
-
* @param clear
|
|
151
|
-
*/
|
|
152
|
-
allocate(numBytes: number, clear?: boolean): number;
|
|
153
|
-
/**
|
|
154
|
-
* Frees a previous allocated memory region using the exported WASM core API
|
|
155
|
-
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
156
|
-
* `numBytes` value must be the same as previously given to
|
|
157
|
-
* {@link WasmBridge.allocate}.
|
|
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
|
-
*
|
|
163
|
-
* @param addr
|
|
164
|
-
* @param numBytes
|
|
165
|
-
*/
|
|
166
|
-
free(addr: number, numBytes: number): void;
|
|
139
|
+
allocate(numBytes: number, clear?: boolean): MemorySlice;
|
|
140
|
+
free([addr, numBytes]: MemorySlice): void;
|
|
167
141
|
getI8(addr: number): number;
|
|
168
142
|
getU8(addr: number): number;
|
|
169
143
|
getI16(addr: number): number;
|
package/bridge.js
CHANGED
|
@@ -198,19 +198,6 @@ let WasmBridge = class WasmBridge {
|
|
|
198
198
|
this.exports.memory.grow(numPages);
|
|
199
199
|
this.ensureMemory();
|
|
200
200
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Attempts to allocate `numBytes` using the exported WASM core API function
|
|
203
|
-
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
|
|
204
|
-
* start address of the new memory block. If unsuccessful, throws an
|
|
205
|
-
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
|
|
206
|
-
* be zero-filled.
|
|
207
|
-
*
|
|
208
|
-
* @remarks
|
|
209
|
-
* See {@link WasmExports._wasm_allocate} docs for further details.
|
|
210
|
-
*
|
|
211
|
-
* @param numBytes
|
|
212
|
-
* @param clear
|
|
213
|
-
*/
|
|
214
201
|
allocate(numBytes, clear = false) {
|
|
215
202
|
const addr = this.exports._wasm_allocate(numBytes);
|
|
216
203
|
if (!addr)
|
|
@@ -218,22 +205,9 @@ let WasmBridge = class WasmBridge {
|
|
|
218
205
|
this.logger.fine(() => `allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
219
206
|
this.ensureMemory();
|
|
220
207
|
clear && this.u8.fill(0, addr, addr + numBytes);
|
|
221
|
-
return addr;
|
|
208
|
+
return [addr, numBytes];
|
|
222
209
|
}
|
|
223
|
-
|
|
224
|
-
* Frees a previous allocated memory region using the exported WASM core API
|
|
225
|
-
* function {@link WasmExports._wasm_free} (implementation specific). The
|
|
226
|
-
* `numBytes` value must be the same as previously given to
|
|
227
|
-
* {@link WasmBridge.allocate}.
|
|
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
|
-
*
|
|
233
|
-
* @param addr
|
|
234
|
-
* @param numBytes
|
|
235
|
-
*/
|
|
236
|
-
free(addr, numBytes) {
|
|
210
|
+
free([addr, numBytes]) {
|
|
237
211
|
this.logger.fine(() => `freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
|
|
238
212
|
this.exports._wasm_free(addr, numBytes);
|
|
239
213
|
}
|
package/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
|
|
2
|
-
import { isArray, isPlainObject } from "@thi.ng/checks";
|
|
2
|
+
import { isArray, isPlainObject, isString } from "@thi.ng/checks";
|
|
3
3
|
import { illegalArgs } from "@thi.ng/errors";
|
|
4
4
|
import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
|
|
5
5
|
import { ConsoleLogger } from "@thi.ng/logger";
|
|
@@ -84,7 +84,7 @@ const addTypeSpec = (ctx, path, coll, spec) => {
|
|
|
84
84
|
invalidSpec(path, `${spec.name}.body must be an object`);
|
|
85
85
|
for (let lang in spec.body) {
|
|
86
86
|
const src = spec.body[lang];
|
|
87
|
-
if (src[0] === "@") {
|
|
87
|
+
if (isString(src) && src[0] === "@") {
|
|
88
88
|
spec.body[lang] = readText(src.substring(1), ctx.logger);
|
|
89
89
|
}
|
|
90
90
|
}
|
package/codegen/typescript.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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
3
|
import { PKG_NAME, } from "../api.js";
|
|
4
|
-
import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
|
|
4
|
+
import { ensureLines, enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
|
|
5
|
+
import { fieldType as zigFieldType } from "./zig.js";
|
|
5
6
|
/**
|
|
6
7
|
* TypeScript code generator. Call with options and then pass to
|
|
7
8
|
* {@link generateTypes} (see its docs for further usage).
|
|
@@ -22,7 +23,7 @@ export const TYPESCRIPT = (opts = {}) => {
|
|
|
22
23
|
const SCOPES = [/\{$/, /\}\)?[;,]?$/];
|
|
23
24
|
const gen = {
|
|
24
25
|
pre: (opts) => `// @ts-ignore possibly includes unused imports
|
|
25
|
-
import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
|
|
26
|
+
import { MemorySlice, Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
|
|
26
27
|
post: () => opts.post || "",
|
|
27
28
|
doc: (doc, acc, opts) => {
|
|
28
29
|
acc.push("/**", ...prefixLines(" * ", doc, opts.lineWidth), " */");
|
|
@@ -55,11 +56,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
55
56
|
for (let f of struct.fields) {
|
|
56
57
|
if (isPadding(f))
|
|
57
58
|
continue;
|
|
58
|
-
|
|
59
|
+
const doc = __docType(struct, f, opts);
|
|
60
|
+
doc && gen.doc(doc, lines, opts);
|
|
59
61
|
const ftype = __fieldType(f, opts);
|
|
60
62
|
fieldTypes[f.name] = ftype;
|
|
61
63
|
lines.push(`${f.name}: ${ftype};`);
|
|
62
64
|
}
|
|
65
|
+
if (struct.body?.ts) {
|
|
66
|
+
lines.push("", ...ensureLines(struct.body.ts, "decl"));
|
|
67
|
+
}
|
|
63
68
|
lines.push("}", "");
|
|
64
69
|
const pointerDecls = pointerFields(struct.fields).map((x) => {
|
|
65
70
|
return `let $${x.name}: ${fieldTypes[x.name]} | null = null;`;
|
|
@@ -159,6 +164,9 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
159
164
|
// close field accessor
|
|
160
165
|
lines.push(`},`);
|
|
161
166
|
}
|
|
167
|
+
if (struct.body?.ts) {
|
|
168
|
+
lines.push("", ...ensureLines(struct.body.ts, "impl"), "");
|
|
169
|
+
}
|
|
162
170
|
lines.push("};", "}", "});", "");
|
|
163
171
|
acc.push(...withIndentation(lines, indent, ...SCOPES));
|
|
164
172
|
},
|
|
@@ -221,3 +229,12 @@ const __mapStringArray = (target, name, type, len, isConst, isLocal = false) =>
|
|
|
221
229
|
`for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
|
|
222
230
|
`return $${name};`,
|
|
223
231
|
];
|
|
232
|
+
const __docType = (parent, f, opts) => {
|
|
233
|
+
const doc = [...ensureLines(f.doc || [])];
|
|
234
|
+
if (isWasmPrim(f.type)) {
|
|
235
|
+
if (doc.length)
|
|
236
|
+
doc.push("");
|
|
237
|
+
doc.push(`WASM type: ${zigFieldType(parent, f, opts).type}`);
|
|
238
|
+
}
|
|
239
|
+
return doc.length ? doc : undefined;
|
|
240
|
+
};
|
package/codegen/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BigType } from "@thi.ng/api";
|
|
2
|
-
import type { CodeGenOpts, Field, WasmPrim, WasmPrim32 } from "../api.js";
|
|
2
|
+
import type { CodeGenOpts, Field, InjectedBody, WasmPrim, WasmPrim32 } from "../api.js";
|
|
3
3
|
/**
|
|
4
4
|
* Returns true iff `x` is a {@link WasmPrim32}.
|
|
5
5
|
*
|
|
@@ -28,16 +28,6 @@ export declare const isSlice: (f: Field) => boolean;
|
|
|
28
28
|
* @param f
|
|
29
29
|
*/
|
|
30
30
|
export declare const isPointerLike: (f: Field) => boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Takes an array of strings or splits given string into lines, word wraps and
|
|
33
|
-
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
34
|
-
* lines.
|
|
35
|
-
*
|
|
36
|
-
* @param prefix
|
|
37
|
-
* @param str
|
|
38
|
-
* @param width
|
|
39
|
-
*/
|
|
40
|
-
export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
|
|
41
31
|
/**
|
|
42
32
|
* Returns true if `type` is "slice".
|
|
43
33
|
*
|
|
@@ -69,6 +59,25 @@ export declare const stringFields: (fields: Field[]) => Field[];
|
|
|
69
59
|
* @internal
|
|
70
60
|
*/
|
|
71
61
|
export declare const enumName: (opts: CodeGenOpts, name: string) => string;
|
|
62
|
+
/**
|
|
63
|
+
* Returns given field's default value (or undefined). The `lang` ID is required
|
|
64
|
+
* to obtain the language specific value if the default is given as object.
|
|
65
|
+
*
|
|
66
|
+
* @param f
|
|
67
|
+
* @param lang
|
|
68
|
+
*/
|
|
69
|
+
export declare const defaultValue: (f: Field, lang: string) => import("@thi.ng/api").NumOrString | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Takes an array of strings or splits given string into lines, word wraps and
|
|
72
|
+
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
73
|
+
* lines.
|
|
74
|
+
*
|
|
75
|
+
* @param prefix
|
|
76
|
+
* @param str
|
|
77
|
+
* @param width
|
|
78
|
+
*/
|
|
79
|
+
export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
|
|
80
|
+
export declare const ensureLines: (src: string | string[] | InjectedBody, key?: keyof InjectedBody) => Iterable<string>;
|
|
72
81
|
/**
|
|
73
82
|
* Yields iterator of given lines, each with applied indentation based on given
|
|
74
83
|
* scope regexp's which are applied to each line to increase or decrease
|
package/codegen/utils.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { isArray } from "@thi.ng/checks/is-array";
|
|
2
|
+
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
|
|
1
3
|
import { isString } from "@thi.ng/checks/is-string";
|
|
4
|
+
import { split } from "@thi.ng/strings/split";
|
|
2
5
|
import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
|
|
3
6
|
/**
|
|
4
7
|
* Returns true iff `x` is a {@link WasmPrim32}.
|
|
@@ -28,18 +31,6 @@ export const isSlice = (f) => f.tag === "slice";
|
|
|
28
31
|
* @param f
|
|
29
32
|
*/
|
|
30
33
|
export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
|
|
31
|
-
/**
|
|
32
|
-
* Takes an array of strings or splits given string into lines, word wraps and
|
|
33
|
-
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
34
|
-
* lines.
|
|
35
|
-
*
|
|
36
|
-
* @param prefix
|
|
37
|
-
* @param str
|
|
38
|
-
* @param width
|
|
39
|
-
*/
|
|
40
|
-
export const prefixLines = (prefix, str, width) => (isString(str)
|
|
41
|
-
? wordWrapLines(str, { width: width - prefix.length })
|
|
42
|
-
: str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
|
|
43
34
|
/**
|
|
44
35
|
* Returns true if `type` is "slice".
|
|
45
36
|
*
|
|
@@ -71,6 +62,39 @@ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type
|
|
|
71
62
|
* @internal
|
|
72
63
|
*/
|
|
73
64
|
export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
|
|
65
|
+
/**
|
|
66
|
+
* Returns given field's default value (or undefined). The `lang` ID is required
|
|
67
|
+
* to obtain the language specific value if the default is given as object.
|
|
68
|
+
*
|
|
69
|
+
* @param f
|
|
70
|
+
* @param lang
|
|
71
|
+
*/
|
|
72
|
+
export const defaultValue = (f, lang) => f.default !== undefined
|
|
73
|
+
? isPlainObject(f.default)
|
|
74
|
+
? f.default[lang]
|
|
75
|
+
: f.default
|
|
76
|
+
: undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Takes an array of strings or splits given string into lines, word wraps and
|
|
79
|
+
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
80
|
+
* lines.
|
|
81
|
+
*
|
|
82
|
+
* @param prefix
|
|
83
|
+
* @param str
|
|
84
|
+
* @param width
|
|
85
|
+
*/
|
|
86
|
+
export const prefixLines = (prefix, str, width) => (isString(str)
|
|
87
|
+
? wordWrapLines(str, { width: width - prefix.length })
|
|
88
|
+
: str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
|
|
89
|
+
export const ensureLines = (src, key) => isString(src)
|
|
90
|
+
? split(src)
|
|
91
|
+
: isArray(src)
|
|
92
|
+
? src
|
|
93
|
+
: key
|
|
94
|
+
? src[key]
|
|
95
|
+
? ensureLines(src[key], key)
|
|
96
|
+
: []
|
|
97
|
+
: [];
|
|
74
98
|
/**
|
|
75
99
|
* Yields iterator of given lines, each with applied indentation based on given
|
|
76
100
|
* scope regexp's which are applied to each line to increase or decrease
|
package/codegen/zig.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodeGenOptsBase, ICodeGen } from "../api.js";
|
|
1
|
+
import type { CodeGenOpts, CodeGenOptsBase, Field, ICodeGen, Struct, Union } from "../api.js";
|
|
2
2
|
/**
|
|
3
3
|
* Zig code generator options.
|
|
4
4
|
*/
|
|
@@ -15,4 +15,9 @@ export interface ZigOpts extends CodeGenOptsBase {
|
|
|
15
15
|
* @param opts
|
|
16
16
|
*/
|
|
17
17
|
export declare const ZIG: (opts?: Partial<ZigOpts>) => ICodeGen;
|
|
18
|
+
/** @internal */
|
|
19
|
+
export declare const fieldType: (parent: Struct | Union, f: Field, opts: CodeGenOpts) => {
|
|
20
|
+
type: string;
|
|
21
|
+
defaultVal: string;
|
|
22
|
+
};
|
|
18
23
|
//# sourceMappingURL=zig.d.ts.map
|
package/codegen/zig.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { isNumber } from "@thi.ng/checks
|
|
1
|
+
import { isNumber } from "@thi.ng/checks";
|
|
2
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
3
|
import { unsupported } from "@thi.ng/errors/unsupported";
|
|
4
|
-
import {
|
|
5
|
-
import { enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
|
|
4
|
+
import { defaultValue, ensureLines, enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
|
|
6
5
|
/**
|
|
7
6
|
* Zig code generator. Call with options and then pass to {@link generateTypes}
|
|
8
7
|
* (see its docs for further usage).
|
|
@@ -39,7 +38,7 @@ export const ZIG = (opts = {}) => {
|
|
|
39
38
|
lines.push(line + ",");
|
|
40
39
|
}
|
|
41
40
|
if (e.body?.zig) {
|
|
42
|
-
lines.push("", ...
|
|
41
|
+
lines.push("", ...ensureLines(e.body.zig, "impl"), "");
|
|
43
42
|
}
|
|
44
43
|
lines.push("};", "");
|
|
45
44
|
acc.push(...withIndentation(lines, INDENT, ...SCOPES));
|
|
@@ -75,46 +74,12 @@ const __generateFields = (gen, parent, opts) => {
|
|
|
75
74
|
continue;
|
|
76
75
|
}
|
|
77
76
|
f.doc && gen.doc(f.doc, res, opts);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? "[]const u8"
|
|
82
|
-
: "[]u8"
|
|
83
|
-
: f.const !== false
|
|
84
|
-
? "[*:0]const u8"
|
|
85
|
-
: "[*:0]u8"
|
|
86
|
-
: f.type;
|
|
87
|
-
let defaultVal = "";
|
|
88
|
-
switch (f.tag) {
|
|
89
|
-
case "array":
|
|
90
|
-
ftype =
|
|
91
|
-
f.sentinel !== undefined
|
|
92
|
-
? `[${f.len}:${f.sentinel}]${ftype}`
|
|
93
|
-
: `[${f.len}]${ftype}`;
|
|
94
|
-
break;
|
|
95
|
-
case "slice":
|
|
96
|
-
ftype = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${ftype}`;
|
|
97
|
-
break;
|
|
98
|
-
case "vec":
|
|
99
|
-
ftype = `@Vector(${f.len}, ${ftype})`;
|
|
100
|
-
break;
|
|
101
|
-
case "ptr":
|
|
102
|
-
ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
|
|
103
|
-
break;
|
|
104
|
-
case "scalar":
|
|
105
|
-
default:
|
|
106
|
-
if (f.default != undefined) {
|
|
107
|
-
if (!(isString(f.default) || isNumber(f.default))) {
|
|
108
|
-
unsupported(`wrong default value for ${name}.${f.name} (${f.default})`);
|
|
109
|
-
}
|
|
110
|
-
defaultVal = ` = ${JSON.stringify(f.default)}`;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
ftypes[f.name] = ftype;
|
|
114
|
-
res.push(`${f.name}: ${ftype}${defaultVal},`);
|
|
77
|
+
const { type, defaultVal } = fieldType(parent, f, opts);
|
|
78
|
+
ftypes[f.name] = type;
|
|
79
|
+
res.push(`${f.name}: ${type}${defaultVal},`);
|
|
115
80
|
}
|
|
116
81
|
if (parent.body?.zig) {
|
|
117
|
-
res.push("", ...
|
|
82
|
+
res.push("", ...ensureLines(parent.body.zig, "impl"), "");
|
|
118
83
|
}
|
|
119
84
|
res.push("};");
|
|
120
85
|
if (opts.debug) {
|
|
@@ -133,6 +98,47 @@ const __generateFields = (gen, parent, opts) => {
|
|
|
133
98
|
res.push("");
|
|
134
99
|
return res;
|
|
135
100
|
};
|
|
101
|
+
/** @internal */
|
|
102
|
+
export const fieldType = (parent, f, opts) => {
|
|
103
|
+
let type = isWasmString(f.type)
|
|
104
|
+
? isStringSlice(opts.stringType)
|
|
105
|
+
? f.const !== false
|
|
106
|
+
? "[]const u8"
|
|
107
|
+
: "[]u8"
|
|
108
|
+
: f.const !== false
|
|
109
|
+
? "[*:0]const u8"
|
|
110
|
+
: "[*:0]u8"
|
|
111
|
+
: f.type;
|
|
112
|
+
let defaultVal = defaultValue(f, "zig");
|
|
113
|
+
switch (f.tag) {
|
|
114
|
+
case "array":
|
|
115
|
+
type =
|
|
116
|
+
f.sentinel !== undefined
|
|
117
|
+
? `[${f.len}:${f.sentinel}]${type}`
|
|
118
|
+
: `[${f.len}]${type}`;
|
|
119
|
+
break;
|
|
120
|
+
case "slice":
|
|
121
|
+
type = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${type}`;
|
|
122
|
+
break;
|
|
123
|
+
case "vec":
|
|
124
|
+
type = `@Vector(${f.len}, ${type})`;
|
|
125
|
+
break;
|
|
126
|
+
case "ptr":
|
|
127
|
+
type = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${type}`;
|
|
128
|
+
break;
|
|
129
|
+
case "scalar":
|
|
130
|
+
default:
|
|
131
|
+
if (defaultVal != undefined) {
|
|
132
|
+
if (!(isString(defaultVal) || isNumber(defaultVal))) {
|
|
133
|
+
unsupported(`wrong default value for ${parent.name}.${f.name} (${defaultVal})`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
type,
|
|
139
|
+
defaultVal: defaultVal != undefined ? ` = ${defaultVal}` : "",
|
|
140
|
+
};
|
|
141
|
+
};
|
|
136
142
|
const __packedPadding = (id, n, res) => {
|
|
137
143
|
let i = 0;
|
|
138
144
|
n <<= 3;
|
package/codegen.js
CHANGED
|
@@ -113,13 +113,13 @@ const alignOf = defmulti((x) => x.type, {}, {
|
|
|
113
113
|
});
|
|
114
114
|
const prepareType = defmulti((x) => x.type, {}, {
|
|
115
115
|
[DEFAULT]: (x, types, alignImpl, opts) => {
|
|
116
|
-
if (x.__align
|
|
116
|
+
if (x.__align)
|
|
117
117
|
return;
|
|
118
118
|
alignOf(x, types, alignImpl, opts);
|
|
119
119
|
sizeOf(x, types, alignImpl, opts);
|
|
120
120
|
},
|
|
121
121
|
struct: (x, types, align, opts) => {
|
|
122
|
-
if (x.__align
|
|
122
|
+
if (x.__align)
|
|
123
123
|
return;
|
|
124
124
|
const struct = x;
|
|
125
125
|
alignOf(struct, types, align, opts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/wasm-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Generic, modular, extensible API bridge, polyglot glue code and bindings code generators for hybrid JS & WebAssembly projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -36,27 +36,27 @@
|
|
|
36
36
|
"test:build-zig": "zig build-lib -O ReleaseSmall -target wasm32-freestanding -dynamic --strip --pkg-begin wasmapi zig/wasmapi.zig --pkg-end test/custom.zig && wasm-dis -o custom.wast custom.wasm && cp custom.wasm test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@thi.ng/api": "^8.4.
|
|
40
|
-
"@thi.ng/args": "^2.2.
|
|
41
|
-
"@thi.ng/binary": "^3.3.
|
|
42
|
-
"@thi.ng/checks": "^3.3.
|
|
43
|
-
"@thi.ng/compare": "^2.1.
|
|
44
|
-
"@thi.ng/defmulti": "^2.1.
|
|
45
|
-
"@thi.ng/errors": "^2.2.
|
|
46
|
-
"@thi.ng/file-io": "^0.3.
|
|
47
|
-
"@thi.ng/hex": "^2.2.
|
|
48
|
-
"@thi.ng/idgen": "^2.1.
|
|
49
|
-
"@thi.ng/logger": "^1.4.
|
|
50
|
-
"@thi.ng/paths": "^5.1.
|
|
51
|
-
"@thi.ng/strings": "^3.3.
|
|
39
|
+
"@thi.ng/api": "^8.4.4",
|
|
40
|
+
"@thi.ng/args": "^2.2.7",
|
|
41
|
+
"@thi.ng/binary": "^3.3.8",
|
|
42
|
+
"@thi.ng/checks": "^3.3.2",
|
|
43
|
+
"@thi.ng/compare": "^2.1.14",
|
|
44
|
+
"@thi.ng/defmulti": "^2.1.19",
|
|
45
|
+
"@thi.ng/errors": "^2.2.3",
|
|
46
|
+
"@thi.ng/file-io": "^0.3.17",
|
|
47
|
+
"@thi.ng/hex": "^2.2.2",
|
|
48
|
+
"@thi.ng/idgen": "^2.1.16",
|
|
49
|
+
"@thi.ng/logger": "^1.4.2",
|
|
50
|
+
"@thi.ng/paths": "^5.1.20",
|
|
51
|
+
"@thi.ng/strings": "^3.3.15"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@microsoft/api-extractor": "^7.
|
|
55
|
-
"@thi.ng/testament": "^0.3.
|
|
54
|
+
"@microsoft/api-extractor": "^7.33.5",
|
|
55
|
+
"@thi.ng/testament": "^0.3.4",
|
|
56
56
|
"rimraf": "^3.0.2",
|
|
57
57
|
"tools": "^0.0.1",
|
|
58
|
-
"typedoc": "^0.
|
|
59
|
-
"typescript": "^4.8.
|
|
58
|
+
"typedoc": "^0.23.18",
|
|
59
|
+
"typescript": "^4.8.4"
|
|
60
60
|
},
|
|
61
61
|
"keywords": [
|
|
62
62
|
"allocator",
|
|
@@ -141,5 +141,5 @@
|
|
|
141
141
|
"status": "alpha",
|
|
142
142
|
"year": 2022
|
|
143
143
|
},
|
|
144
|
-
"gitHead": "
|
|
144
|
+
"gitHead": "0402675247b1c312e4dd3d9bc6c01af5cc837c5d\n"
|
|
145
145
|
}
|