@bytecodealliance/jco 1.9.1 → 1.10.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/lib/wasi_snapshot_preview1.command.wasm +0 -0
- package/lib/wasi_snapshot_preview1.reactor.wasm +0 -0
- package/obj/interfaces/local-wasm-tools-tools.d.ts +1 -0
- package/obj/interfaces/wasi-cli-terminal-input.d.ts +4 -0
- package/obj/interfaces/wasi-cli-terminal-output.d.ts +4 -0
- package/obj/interfaces/wasi-filesystem-types.d.ts +8 -0
- package/obj/interfaces/wasi-io-error.d.ts +4 -0
- package/obj/interfaces/wasi-io-streams.d.ts +8 -0
- package/obj/js-component-bindgen-component.core.wasm +0 -0
- package/obj/js-component-bindgen-component.core2.wasm +0 -0
- package/obj/js-component-bindgen-component.d.ts +14 -0
- package/obj/js-component-bindgen-component.js +515 -389
- package/obj/wasm-tools.core.wasm +0 -0
- package/obj/wasm-tools.core2.wasm +0 -0
- package/obj/wasm-tools.js +432 -375
- package/package.json +5 -5
- package/src/cmd/componentize.d.ts.map +1 -1
- package/src/cmd/opt.d.ts +7 -2
- package/src/cmd/opt.d.ts.map +1 -1
- package/src/cmd/opt.js +196 -82
- package/src/cmd/run.js +3 -3
- package/src/cmd/transpile.d.ts +15 -0
- package/src/cmd/transpile.d.ts.map +1 -1
- package/src/cmd/transpile.js +56 -0
- package/src/jco.js +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytecodealliance/jco",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "JavaScript tooling for working with WebAssembly Components",
|
|
5
5
|
"author": "Guy Bedford",
|
|
6
6
|
"bin": {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@bytecodealliance/componentize-js": "^0.
|
|
27
|
+
"@bytecodealliance/componentize-js": "^0.17.0",
|
|
28
28
|
"@bytecodealliance/preview2-shim": "^0.17.1",
|
|
29
|
-
"binaryen": "^
|
|
29
|
+
"binaryen": "^122.0.0",
|
|
30
30
|
"chalk-template": "^1",
|
|
31
31
|
"commander": "^12",
|
|
32
32
|
"mkdirp": "^3",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"build:release": "cargo xtask build release",
|
|
63
63
|
"build:types:preview2-shim": "cargo xtask generate wasi-types",
|
|
64
64
|
"lint": "eslint -c eslintrc.cjs src/**/*.js packages/*/lib/**/*.js",
|
|
65
|
-
"test:lts": "mocha -u tdd test/test.js --timeout
|
|
66
|
-
"test": "node --stack-trace-limit=100 node_modules/mocha/bin/mocha.js -u tdd test/test.js --timeout
|
|
65
|
+
"test:lts": "mocha -u tdd test/test.js --timeout 240000",
|
|
66
|
+
"test": "node --experimental-wasm-jspi --stack-trace-limit=100 node_modules/mocha/bin/mocha.js -u tdd test/test.js --timeout 240000",
|
|
67
67
|
"prepublishOnly": "cargo xtask build release && npm run test"
|
|
68
68
|
},
|
|
69
69
|
"files": [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"componentize.d.ts","sourceRoot":"","sources":["componentize.js"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"componentize.d.ts","sourceRoot":"","sources":["componentize.js"],"names":[],"mappings":"AAIA,sEAkBC"}
|
package/src/cmd/opt.d.ts
CHANGED
|
@@ -2,10 +2,15 @@ export function opt(componentPath: any, opts: any, program: any): Promise<void>;
|
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @param {Uint8Array} componentBytes
|
|
5
|
-
* @param {{ quiet: boolean, optArgs?: string[] }}
|
|
5
|
+
* @param {{ quiet: boolean, asyncify?: boolean, optArgs?: string[], noVerify?: boolean }} opts?
|
|
6
6
|
* @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
|
|
7
7
|
*/
|
|
8
|
-
export function optimizeComponent(componentBytes: Uint8Array, opts:
|
|
8
|
+
export function optimizeComponent(componentBytes: Uint8Array, opts: {
|
|
9
|
+
quiet: boolean;
|
|
10
|
+
asyncify?: boolean;
|
|
11
|
+
optArgs?: string[];
|
|
12
|
+
noVerify?: boolean;
|
|
13
|
+
}): Promise<{
|
|
9
14
|
component: Uint8Array;
|
|
10
15
|
compressionInfo: {
|
|
11
16
|
beforeBytes: number;
|
package/src/cmd/opt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opt.d.ts","sourceRoot":"","sources":["opt.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"opt.d.ts","sourceRoot":"","sources":["opt.js"],"names":[],"mappings":"AAgBA,gFA2CC;AAgBD;;;;;GAKG;AACH,kDAJW,UAAU,QACV;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,eAAe,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAA,CAAE,CAkK7G"}
|
package/src/cmd/opt.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import { $init, tools } from
|
|
1
|
+
import { $init, tools } from "../../obj/wasm-tools.js";
|
|
2
2
|
const { metadataShow, print } = tools;
|
|
3
|
-
import { writeFile } from
|
|
4
|
-
import { fileURLToPath } from
|
|
5
|
-
import c from
|
|
6
|
-
import {
|
|
7
|
-
|
|
3
|
+
import { writeFile } from "fs/promises";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import c from "chalk-template";
|
|
6
|
+
import {
|
|
7
|
+
readFile,
|
|
8
|
+
sizeStr,
|
|
9
|
+
fixedDigitDisplay,
|
|
10
|
+
table,
|
|
11
|
+
spawnIOTmp,
|
|
12
|
+
setShowSpinner,
|
|
13
|
+
getShowSpinner,
|
|
14
|
+
} from "../common.js";
|
|
15
|
+
import ora from "#ora";
|
|
8
16
|
|
|
9
|
-
export async function opt
|
|
17
|
+
export async function opt(componentPath, opts, program) {
|
|
10
18
|
await $init;
|
|
11
|
-
const varIdx = program.parent.rawArgs.indexOf(
|
|
12
|
-
if (varIdx !== -1)
|
|
13
|
-
opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
|
|
19
|
+
const varIdx = program.parent.rawArgs.indexOf("--");
|
|
20
|
+
if (varIdx !== -1) opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
|
|
14
21
|
const componentBytes = await readFile(componentPath);
|
|
15
22
|
|
|
16
23
|
if (!opts.quiet) setShowSpinner(true);
|
|
@@ -19,128 +26,235 @@ export async function opt (componentPath, opts, program) {
|
|
|
19
26
|
|
|
20
27
|
await writeFile(opts.output, component);
|
|
21
28
|
|
|
22
|
-
let totalBeforeBytes = 0,
|
|
29
|
+
let totalBeforeBytes = 0,
|
|
30
|
+
totalAfterBytes = 0;
|
|
23
31
|
|
|
24
32
|
if (!opts.quiet)
|
|
25
33
|
console.log(c`
|
|
26
34
|
{bold Optimized WebAssembly Component Internal Core Modules:}
|
|
27
35
|
|
|
28
|
-
${table(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
${table(
|
|
37
|
+
[
|
|
38
|
+
...compressionInfo.map(({ beforeBytes, afterBytes }, i) => {
|
|
39
|
+
totalBeforeBytes += beforeBytes;
|
|
40
|
+
totalAfterBytes += afterBytes;
|
|
41
|
+
return [
|
|
42
|
+
` - Core Module ${i + 1}: `,
|
|
43
|
+
sizeStr(beforeBytes),
|
|
44
|
+
" -> ",
|
|
45
|
+
c`{cyan ${sizeStr(afterBytes)}} `,
|
|
46
|
+
`(${fixedDigitDisplay((afterBytes / beforeBytes) * 100, 2)}%)`,
|
|
47
|
+
];
|
|
48
|
+
}),
|
|
49
|
+
["", "", "", "", ""],
|
|
50
|
+
[
|
|
51
|
+
` = Total: `,
|
|
52
|
+
`${sizeStr(totalBeforeBytes)}`,
|
|
53
|
+
` => `,
|
|
54
|
+
c`{cyan ${sizeStr(totalAfterBytes)}} `,
|
|
55
|
+
`(${fixedDigitDisplay((totalAfterBytes / totalBeforeBytes) * 100, 2)}%)`,
|
|
56
|
+
],
|
|
57
|
+
],
|
|
58
|
+
[, , , , "right"]
|
|
59
|
+
)}`);
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
/**
|
|
48
|
-
*
|
|
49
|
-
* @param {
|
|
50
|
-
* @
|
|
63
|
+
* Counts the byte length for the LEB128 encoding of a number.
|
|
64
|
+
* @param {number} val
|
|
65
|
+
* @returns {number}
|
|
66
|
+
*/
|
|
67
|
+
function byteLengthLEB128(val) {
|
|
68
|
+
let len = 0;
|
|
69
|
+
do {
|
|
70
|
+
val >>>= 7;
|
|
71
|
+
len++;
|
|
72
|
+
} while (val !== 0);
|
|
73
|
+
return len;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
*
|
|
78
|
+
* @param {Uint8Array} componentBytes
|
|
79
|
+
* @param {{ quiet: boolean, asyncify?: boolean, optArgs?: string[], noVerify?: boolean }} opts?
|
|
51
80
|
* @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
|
|
52
81
|
*/
|
|
53
|
-
export async function optimizeComponent
|
|
82
|
+
export async function optimizeComponent(componentBytes, opts) {
|
|
54
83
|
await $init;
|
|
55
84
|
const showSpinner = getShowSpinner();
|
|
56
85
|
let spinner;
|
|
57
86
|
try {
|
|
58
|
-
|
|
87
|
+
let componentMetadata = metadataShow(componentBytes);
|
|
88
|
+
componentMetadata.forEach((metadata, index) => {
|
|
89
|
+
// add index to the metadata object
|
|
90
|
+
metadata.index = index;
|
|
91
|
+
const size = metadata.range[1] - metadata.range[0];
|
|
92
|
+
// compute previous LEB128 encoding length
|
|
93
|
+
metadata.prevLEBLen = byteLengthLEB128(size);
|
|
94
|
+
});
|
|
95
|
+
const coreModules = componentMetadata
|
|
96
|
+
.filter(({ metaType }) => metaType.tag === "module");
|
|
59
97
|
|
|
98
|
+
// log number of core Wasm modules to be run with wasm-opt
|
|
60
99
|
let completed = 0;
|
|
61
|
-
const spinnerText = () =>
|
|
100
|
+
const spinnerText = () =>
|
|
101
|
+
c`{cyan ${completed} / ${coreModules.length}} Running Binaryen on WebAssembly Component Internal Core Modules \n`;
|
|
62
102
|
if (showSpinner) {
|
|
63
103
|
spinner = ora({
|
|
64
|
-
color:
|
|
65
|
-
spinner:
|
|
104
|
+
color: "cyan",
|
|
105
|
+
spinner: "bouncingBar",
|
|
66
106
|
}).start();
|
|
67
107
|
spinner.text = spinnerText();
|
|
68
108
|
}
|
|
69
109
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
110
|
+
// gather the options for wasm-opt. optionally, adding the asyncify flag
|
|
111
|
+
const args = opts?.optArgs ?
|
|
112
|
+
[...opts.optArgs] :
|
|
113
|
+
['-Oz', '--low-memory-unused', '--enable-bulk-memory', '--strip-debug'];
|
|
114
|
+
if (opts?.asyncify) args.push('--asyncify');
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// process core Wasm modules with wasm-opt
|
|
118
|
+
await Promise.all(
|
|
119
|
+
coreModules.map(async (metadata) => {
|
|
120
|
+
if (metadata.metaType.tag === "module") {
|
|
121
|
+
// store the wasm-opt processed module in the metadata
|
|
122
|
+
metadata.optimized = await wasmOpt(
|
|
123
|
+
componentBytes.subarray(metadata.range[0], metadata.range[1]),
|
|
124
|
+
args);
|
|
125
|
+
|
|
126
|
+
// compute the size change, including the change to
|
|
127
|
+
// the LEB128 encoding of the size change
|
|
128
|
+
const prevModuleSize = metadata.range[1] - metadata.range[0];
|
|
129
|
+
const newModuleSize = metadata.optimized.byteLength;
|
|
130
|
+
metadata.newLEBLen = byteLengthLEB128(newModuleSize);
|
|
131
|
+
metadata.sizeChange = newModuleSize - prevModuleSize;
|
|
132
|
+
|
|
133
|
+
if (spinner) {
|
|
134
|
+
completed++;
|
|
135
|
+
spinner.text = spinnerText();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
// organize components in modules into tree parent and children
|
|
141
|
+
const nodes = componentMetadata.slice(1);
|
|
142
|
+
const getChildren = (parentIndex) => {
|
|
143
|
+
const children = [];
|
|
144
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
145
|
+
const metadata = nodes[i];
|
|
146
|
+
if (metadata.parentIndex === parentIndex) {
|
|
147
|
+
nodes.splice(i, 1); // remove from nodes
|
|
148
|
+
i--;
|
|
149
|
+
metadata.children = getChildren(metadata.index);
|
|
150
|
+
metadata.sizeChange = metadata.children
|
|
151
|
+
.reduce((total, {prevLEBLen, newLEBLen, sizeChange}) => {
|
|
152
|
+
return sizeChange ?
|
|
153
|
+
total + sizeChange + newLEBLen - prevLEBLen :
|
|
154
|
+
total;
|
|
155
|
+
},
|
|
156
|
+
metadata.sizeChange || 0);
|
|
157
|
+
const prevSize = metadata.range[1] - metadata.range[0];
|
|
158
|
+
metadata.newLEBLen = byteLengthLEB128(prevSize + metadata.sizeChange);
|
|
159
|
+
children.push(metadata);
|
|
160
|
+
}
|
|
75
161
|
}
|
|
76
|
-
return
|
|
77
|
-
}
|
|
162
|
+
return children;
|
|
163
|
+
};
|
|
164
|
+
const componentTree = getChildren(0);
|
|
78
165
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
166
|
+
// compute the total size change in the component binary
|
|
167
|
+
const sizeChange = componentTree
|
|
168
|
+
.reduce((total, {prevLEBLen, newLEBLen, sizeChange}) => {
|
|
169
|
+
return total + (sizeChange || 0) + newLEBLen - prevLEBLen;
|
|
170
|
+
}, 0);
|
|
84
171
|
|
|
85
|
-
|
|
86
|
-
|
|
172
|
+
let outComponentBytes = new Uint8Array(
|
|
173
|
+
componentBytes.byteLength + sizeChange);
|
|
174
|
+
let nextReadPos = 0, nextWritePos = 0;
|
|
87
175
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
176
|
+
const write = ({prevLEBLen, range, optimized, children, sizeChange}) => {
|
|
177
|
+
// write from the last read to the LEB byte start
|
|
178
|
+
outComponentBytes.set(
|
|
179
|
+
componentBytes.subarray(nextReadPos, range[0] - prevLEBLen),
|
|
180
|
+
nextWritePos
|
|
181
|
+
);
|
|
182
|
+
nextWritePos += range[0] - prevLEBLen - nextReadPos;
|
|
91
183
|
|
|
92
|
-
//
|
|
93
|
-
let val =
|
|
184
|
+
// write the new LEB bytes
|
|
185
|
+
let val = range[1] - range[0] + sizeChange;
|
|
94
186
|
do {
|
|
95
|
-
const byte = val &
|
|
187
|
+
const byte = val & 0x7f;
|
|
96
188
|
val >>>= 7;
|
|
97
189
|
outComponentBytes[nextWritePos++] = val === 0 ? byte : byte | 0x80;
|
|
98
190
|
} while (val !== 0);
|
|
99
191
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
192
|
+
if (optimized) {
|
|
193
|
+
// write the core module
|
|
194
|
+
outComponentBytes.set(optimized, nextWritePos);
|
|
195
|
+
nextReadPos = range[1];
|
|
196
|
+
nextWritePos += optimized.byteLength;
|
|
197
|
+
} else if (children.length > 0) {
|
|
198
|
+
// write child components / modules
|
|
199
|
+
nextReadPos = range[0];
|
|
200
|
+
children.forEach(write);
|
|
201
|
+
} else {
|
|
202
|
+
// write component
|
|
203
|
+
outComponentBytes.set(
|
|
204
|
+
componentBytes.subarray(range[0], range[1]),
|
|
205
|
+
nextWritePos
|
|
206
|
+
);
|
|
207
|
+
nextReadPos = range[1];
|
|
208
|
+
nextWritePos += range[1] - range[0];
|
|
209
|
+
}
|
|
210
|
+
};
|
|
106
211
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
nextReadPos += componentBytes.byteLength - nextReadPos;
|
|
212
|
+
// write each top-level component / module
|
|
213
|
+
componentTree.forEach(write);
|
|
110
214
|
|
|
111
|
-
|
|
215
|
+
// write remaining
|
|
216
|
+
outComponentBytes.set(
|
|
217
|
+
componentBytes.subarray(nextReadPos),
|
|
218
|
+
nextWritePos
|
|
219
|
+
);
|
|
112
220
|
|
|
113
221
|
// verify it still parses ok
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
222
|
+
if (!opts?.noVerify) {
|
|
223
|
+
try {
|
|
224
|
+
await print(outComponentBytes);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Internal error performing optimization.\n${e.message}`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
118
230
|
}
|
|
119
231
|
|
|
120
232
|
return {
|
|
121
233
|
component: outComponentBytes,
|
|
122
|
-
compressionInfo: coreModules.map((
|
|
234
|
+
compressionInfo: coreModules.map(({range, optimized}) => ({
|
|
235
|
+
beforeBytes: range[1] - range[0],
|
|
236
|
+
afterBytes: optimized?.byteLength,
|
|
237
|
+
})),
|
|
123
238
|
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (spinner)
|
|
127
|
-
spinner.stop();
|
|
239
|
+
} finally {
|
|
240
|
+
if (spinner) spinner.stop();
|
|
128
241
|
}
|
|
129
242
|
}
|
|
130
243
|
|
|
131
244
|
/**
|
|
132
|
-
* @param {Uint8Array} source
|
|
245
|
+
* @param {Uint8Array} source
|
|
246
|
+
* @param {Array<string>} args
|
|
133
247
|
* @returns {Promise<Uint8Array>}
|
|
134
248
|
*/
|
|
135
|
-
async function wasmOpt(source, args
|
|
136
|
-
const wasmOptPath = fileURLToPath(
|
|
249
|
+
async function wasmOpt(source, args) {
|
|
250
|
+
const wasmOptPath = fileURLToPath(
|
|
251
|
+
import.meta.resolve("binaryen/bin/wasm-opt")
|
|
252
|
+
);
|
|
137
253
|
|
|
138
254
|
try {
|
|
139
|
-
return await spawnIOTmp(wasmOptPath, source, [
|
|
140
|
-
...args, '-o'
|
|
141
|
-
]);
|
|
255
|
+
return await spawnIOTmp(wasmOptPath, source, [...args, "-o"]);
|
|
142
256
|
} catch (e) {
|
|
143
|
-
if (e.toString().includes(
|
|
257
|
+
if (e.toString().includes("BasicBlock requested"))
|
|
144
258
|
return wasmOpt(source, args);
|
|
145
259
|
throw e;
|
|
146
260
|
}
|
package/src/cmd/run.js
CHANGED
|
@@ -40,8 +40,8 @@ export async function serve (componentPath, args, opts) {
|
|
|
40
40
|
return runComponent(componentPath, args, opts, `
|
|
41
41
|
import { HTTPServer } from '@bytecodealliance/preview2-shim/http';
|
|
42
42
|
const server = new HTTPServer(mod.incomingHandler);
|
|
43
|
-
${tryFindPort ? `
|
|
44
43
|
let port = ${port};
|
|
44
|
+
${tryFindPort ? `
|
|
45
45
|
while (true) {
|
|
46
46
|
try {
|
|
47
47
|
server.listen(port, ${JSON.stringify(host)});
|
|
@@ -52,8 +52,8 @@ export async function serve (componentPath, args, opts) {
|
|
|
52
52
|
}
|
|
53
53
|
port++;
|
|
54
54
|
}
|
|
55
|
-
` : `server.listen(
|
|
56
|
-
console.error(\`Server listening on
|
|
55
|
+
` : `server.listen(port, ${JSON.stringify(host)})`}
|
|
56
|
+
console.error(\`Server listening on port...\`);
|
|
57
57
|
`);
|
|
58
58
|
}
|
|
59
59
|
|
package/src/cmd/transpile.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export function types(witPath: any, opts: any): Promise<void>;
|
|
2
|
+
export function guestTypes(witPath: any, opts: any): Promise<void>;
|
|
2
3
|
/**
|
|
3
4
|
* @param {string} witPath
|
|
4
5
|
* @param {{
|
|
@@ -6,8 +7,12 @@ export function types(witPath: any, opts: any): Promise<void>;
|
|
|
6
7
|
* worldName?: string,
|
|
7
8
|
* instantiation?: 'async' | 'sync',
|
|
8
9
|
* tlaCompat?: bool,
|
|
10
|
+
* asyncMode?: string,
|
|
11
|
+
* asyncImports?: string[],
|
|
12
|
+
* asyncExports?: string[],
|
|
9
13
|
* outDir?: string,
|
|
10
14
|
* features?: string[] | 'all',
|
|
15
|
+
* guest?: bool,
|
|
11
16
|
* }} opts
|
|
12
17
|
* @returns {Promise<{ [filename: string]: Uint8Array }>}
|
|
13
18
|
*/
|
|
@@ -16,8 +21,12 @@ export function typesComponent(witPath: string, opts: {
|
|
|
16
21
|
worldName?: string;
|
|
17
22
|
instantiation?: "async" | "sync";
|
|
18
23
|
tlaCompat?: bool;
|
|
24
|
+
asyncMode?: string;
|
|
25
|
+
asyncImports?: string[];
|
|
26
|
+
asyncExports?: string[];
|
|
19
27
|
outDir?: string;
|
|
20
28
|
features?: string[] | "all";
|
|
29
|
+
guest?: bool;
|
|
21
30
|
}): Promise<{
|
|
22
31
|
[filename: string]: Uint8Array;
|
|
23
32
|
}>;
|
|
@@ -30,6 +39,9 @@ export function transpile(componentPath: any, opts: any, program: any): Promise<
|
|
|
30
39
|
* instantiation?: 'async' | 'sync',
|
|
31
40
|
* importBindings?: 'js' | 'optimized' | 'hybrid' | 'direct-optimized',
|
|
32
41
|
* map?: Record<string, string>,
|
|
42
|
+
* asyncMode?: string,
|
|
43
|
+
* asyncImports?: string[],
|
|
44
|
+
* asyncExports?: string[],
|
|
33
45
|
* validLiftingOptimization?: bool,
|
|
34
46
|
* tracing?: bool,
|
|
35
47
|
* nodejsCompat?: bool,
|
|
@@ -51,6 +63,9 @@ export function transpileComponent(component: Uint8Array, opts?: {
|
|
|
51
63
|
instantiation?: "async" | "sync";
|
|
52
64
|
importBindings?: "js" | "optimized" | "hybrid" | "direct-optimized";
|
|
53
65
|
map?: Record<string, string>;
|
|
66
|
+
asyncMode?: string;
|
|
67
|
+
asyncImports?: string[];
|
|
68
|
+
asyncExports?: string[];
|
|
54
69
|
validLiftingOptimization?: bool;
|
|
55
70
|
tracing?: bool;
|
|
56
71
|
nodejsCompat?: bool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["transpile.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["transpile.js"],"names":[],"mappings":"AAgCA,8DAGC;AAED,mEAGC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wCAfW,MAAM,QACN;IACN,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAC5B,KAAK,CAAC,EAAE,IAAI,CAAC;CACd,GACS,OAAO,CAAC;IAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC,CA8CvD;AAkBD,sFA8BC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,8CAzBW,UAAU,SACV;IACN,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,kBAAkB,CAAC;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,wBAAwB,CAAC,EAAE,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,EAAE,CAAC,EAAE,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,sBAAsB,CAAC,EAAE,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GACS,OAAO,CAAC;IAAE,KAAK,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC,EAAE,CAAA;CAAE,CAAC,CAgTnI"}
|
package/src/cmd/transpile.js
CHANGED
|
@@ -14,6 +14,22 @@ import { platform } from 'node:process';
|
|
|
14
14
|
|
|
15
15
|
const isWindows = platform === 'win32';
|
|
16
16
|
|
|
17
|
+
const ASYNC_WASI_IMPORTS = [
|
|
18
|
+
"wasi:io/poll#poll",
|
|
19
|
+
"wasi:io/poll#[method]pollable.block",
|
|
20
|
+
"wasi:io/streams#[method]input-stream.blocking-read",
|
|
21
|
+
"wasi:io/streams#[method]input-stream.blocking-skip",
|
|
22
|
+
"wasi:io/streams#[method]output-stream.blocking-flush",
|
|
23
|
+
"wasi:io/streams#[method]output-stream.blocking-write-and-flush",
|
|
24
|
+
"wasi:io/streams#[method]output-stream.blocking-write-zeroes-and-flush",
|
|
25
|
+
"wasi:io/streams#[method]output-stream.blocking-splice",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const ASYNC_WASI_EXPORTS = [
|
|
29
|
+
"wasi:cli/run#run",
|
|
30
|
+
"wasi:http/incoming-handler#handle",
|
|
31
|
+
];
|
|
32
|
+
|
|
17
33
|
export async function types (witPath, opts) {
|
|
18
34
|
const files = await typesComponent(witPath, opts);
|
|
19
35
|
await writeFiles(files, opts.quiet ? false : 'Generated Type Files');
|
|
@@ -31,6 +47,9 @@ export async function guestTypes (witPath, opts) {
|
|
|
31
47
|
* worldName?: string,
|
|
32
48
|
* instantiation?: 'async' | 'sync',
|
|
33
49
|
* tlaCompat?: bool,
|
|
50
|
+
* asyncMode?: string,
|
|
51
|
+
* asyncImports?: string[],
|
|
52
|
+
* asyncExports?: string[],
|
|
34
53
|
* outDir?: string,
|
|
35
54
|
* features?: string[] | 'all',
|
|
36
55
|
* guest?: bool,
|
|
@@ -57,6 +76,21 @@ export async function typesComponent (witPath, opts) {
|
|
|
57
76
|
features = { tag: 'list', val: opts.feature };
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
if (opts.asyncWasiImports)
|
|
80
|
+
opts.asyncImports = ASYNC_WASI_IMPORTS.concat(opts.asyncImports || []);
|
|
81
|
+
if (opts.asyncWasiExports)
|
|
82
|
+
opts.asyncExports = ASYNC_WASI_EXPORTS.concat(opts.asyncExports || []);
|
|
83
|
+
|
|
84
|
+
const asyncMode = !opts.asyncMode || opts.asyncMode === 'sync' ?
|
|
85
|
+
null :
|
|
86
|
+
{
|
|
87
|
+
tag: opts.asyncMode,
|
|
88
|
+
val: {
|
|
89
|
+
imports: opts.asyncImports || [],
|
|
90
|
+
exports: opts.asyncExports || [],
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
60
94
|
return Object.fromEntries(generateTypes(name, {
|
|
61
95
|
wit: { tag: 'path', val: (isWindows ? '//?/' : '') + resolve(witPath) },
|
|
62
96
|
instantiation,
|
|
@@ -64,6 +98,7 @@ export async function typesComponent (witPath, opts) {
|
|
|
64
98
|
world: opts.worldName,
|
|
65
99
|
features,
|
|
66
100
|
guest: opts.guest ?? false,
|
|
101
|
+
asyncMode,
|
|
67
102
|
}).map(([name, file]) => [`${outDir}${name}`, file]));
|
|
68
103
|
}
|
|
69
104
|
|
|
@@ -105,6 +140,12 @@ export async function transpile (componentPath, opts, program) {
|
|
|
105
140
|
opts.name = basename(componentPath.slice(0, -extname(componentPath).length || Infinity));
|
|
106
141
|
if (opts.map)
|
|
107
142
|
opts.map = Object.fromEntries(opts.map.map(mapping => mapping.split('=')));
|
|
143
|
+
|
|
144
|
+
if (opts.asyncWasiImports)
|
|
145
|
+
opts.asyncImports = ASYNC_WASI_IMPORTS.concat(opts.asyncImports || []);
|
|
146
|
+
if (opts.asyncWasiExports)
|
|
147
|
+
opts.asyncExports = ASYNC_WASI_EXPORTS.concat(opts.asyncExports || []);
|
|
148
|
+
|
|
108
149
|
const { files } = await transpileComponent(component, opts);
|
|
109
150
|
await writeFiles(files, opts.quiet ? false : 'Transpiled JS Component Files');
|
|
110
151
|
}
|
|
@@ -133,6 +174,9 @@ async function wasm2Js (source) {
|
|
|
133
174
|
* instantiation?: 'async' | 'sync',
|
|
134
175
|
* importBindings?: 'js' | 'optimized' | 'hybrid' | 'direct-optimized',
|
|
135
176
|
* map?: Record<string, string>,
|
|
177
|
+
* asyncMode?: string,
|
|
178
|
+
* asyncImports?: string[],
|
|
179
|
+
* asyncExports?: string[],
|
|
136
180
|
* validLiftingOptimization?: bool,
|
|
137
181
|
* tracing?: bool,
|
|
138
182
|
* nodejsCompat?: bool,
|
|
@@ -155,6 +199,7 @@ export async function transpileComponent (component, opts = {}) {
|
|
|
155
199
|
|
|
156
200
|
let spinner;
|
|
157
201
|
const showSpinner = getShowSpinner();
|
|
202
|
+
|
|
158
203
|
if (opts.optimize) {
|
|
159
204
|
if (showSpinner) setShowSpinner(true);
|
|
160
205
|
({ component } = await optimizeComponent(component, opts));
|
|
@@ -183,10 +228,21 @@ export async function transpileComponent (component, opts = {}) {
|
|
|
183
228
|
instantiation = { tag: 'async' };
|
|
184
229
|
}
|
|
185
230
|
|
|
231
|
+
const asyncMode = !opts.asyncMode || opts.asyncMode === 'sync' ?
|
|
232
|
+
null :
|
|
233
|
+
{
|
|
234
|
+
tag: opts.asyncMode,
|
|
235
|
+
val: {
|
|
236
|
+
imports: opts.asyncImports || [],
|
|
237
|
+
exports: opts.asyncExports || [],
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
186
241
|
let { files, imports, exports } = generate(component, {
|
|
187
242
|
name: opts.name ?? 'component',
|
|
188
243
|
map: Object.entries(opts.map ?? {}),
|
|
189
244
|
instantiation,
|
|
245
|
+
asyncMode,
|
|
190
246
|
importBindings: opts.importBindings ? { tag: opts.importBindings } : null,
|
|
191
247
|
validLiftingOptimization: opts.validLiftingOptimization ?? false,
|
|
192
248
|
tracing: opts.tracing ?? false,
|
package/src/jco.js
CHANGED
|
@@ -52,6 +52,11 @@ program.command('transpile')
|
|
|
52
52
|
.option('--no-typescript', 'do not output TypeScript .d.ts types')
|
|
53
53
|
.option('--valid-lifting-optimization', 'optimize component binary validations assuming all lifted values are valid')
|
|
54
54
|
.addOption(new Option('--import-bindings [mode]', 'bindings mode for imports').choices(['js', 'optimized', 'hybrid', 'direct-optimized']).preset('js'))
|
|
55
|
+
.addOption(new Option('--async-mode [mode]', 'EXPERIMENTAL: use async imports and exports').choices(['sync', 'jspi']).preset('sync'))
|
|
56
|
+
.option('--async-wasi-imports', 'EXPERIMENTAL: async component imports from WASI interfaces')
|
|
57
|
+
.option('--async-wasi-exports', 'EXPERIMENTAL: async component exports from WASI interfaces')
|
|
58
|
+
.option('--async-imports <imports...>', 'EXPERIMENTAL: async component imports (examples: "wasi:io/poll@0.2.0#poll", "wasi:io/poll#[method]pollable.block")')
|
|
59
|
+
.option('--async-exports <exports...>', 'EXPERIMENTAL: async component exports (examples: "wasi:cli/run@#run", "handle")')
|
|
55
60
|
.option('--tracing', 'emit `tracing` calls on function entry/exit')
|
|
56
61
|
.option('-b, --base64-cutoff <bytes>', 'set the byte size under which core Wasm binaries will be inlined as base64', myParseInt)
|
|
57
62
|
.option('--tla-compat', 'enables compatibility for JS environments without top-level await support via an async $init promise export')
|
|
@@ -76,6 +81,11 @@ program.command('types')
|
|
|
76
81
|
.requiredOption('-o, --out-dir <out-dir>', 'output directory')
|
|
77
82
|
.option('--tla-compat', 'generates types for the TLA compat output with an async $init promise export')
|
|
78
83
|
.addOption(new Option('-I, --instantiation [mode]', 'type output for custom module instantiation').choices(['async', 'sync']).preset('async'))
|
|
84
|
+
.addOption(new Option('--async-mode [mode]', 'EXPERIMENTAL: use async imports and exports').choices(['sync', 'jspi']).preset('sync'))
|
|
85
|
+
.option('--async-wasi-imports', 'EXPERIMENTAL: async component imports from WASI interfaces')
|
|
86
|
+
.option('--async-wasi-exports', 'EXPERIMENTAL: async component exports from WASI interfaces')
|
|
87
|
+
.option('--async-imports <imports...>', 'EXPERIMENTAL: async component imports (examples: "wasi:io/poll@0.2.0#poll", "wasi:io/poll#[method]pollable.block")')
|
|
88
|
+
.option('--async-exports <exports...>', 'EXPERIMENTAL: async component exports (examples: "wasi:cli/run@#run", "handle")')
|
|
79
89
|
.option('-q, --quiet', 'disable output summary')
|
|
80
90
|
.option('--feature <feature>', 'enable one specific WIT feature (repeatable)', collectOptions, [])
|
|
81
91
|
.option('--all-features', 'enable all features')
|
|
@@ -144,6 +154,7 @@ program.command('opt')
|
|
|
144
154
|
.usage('<component-file> -o <output-file>')
|
|
145
155
|
.argument('<component-file>', 'Wasm component binary filepath')
|
|
146
156
|
.requiredOption('-o, --output <output-file>', 'optimized component output filepath')
|
|
157
|
+
.option('--asyncify', 'runs Asyncify pass in wasm-opt')
|
|
147
158
|
.option('-q, --quiet')
|
|
148
159
|
.option('--', 'custom wasm-opt arguments (defaults to best size optimization)')
|
|
149
160
|
.action(asyncAction(opt));
|