@bytecodealliance/jco 1.10.2 → 1.11.1
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/obj/js-component-bindgen-component.core.wasm +0 -0
- package/obj/js-component-bindgen-component.js +2 -2
- package/obj/wasm-tools.core.wasm +0 -0
- package/obj/wasm-tools.js +2 -2
- package/package.json +18 -6
- package/src/cmd/componentize.js +64 -18
- package/src/cmd/opt.js +233 -202
- package/src/cmd/run.js +117 -73
- package/src/cmd/transpile.d.ts.map +1 -1
- package/src/cmd/transpile.js +643 -530
- package/src/cmd/wasm-tools.js +130 -95
- package/src/jco.js +5 -1
package/src/cmd/opt.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
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
|
|
3
|
+
import { writeFile } from 'fs/promises';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import c from 'chalk-template';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from
|
|
15
|
-
import ora from
|
|
7
|
+
readFile,
|
|
8
|
+
sizeStr,
|
|
9
|
+
fixedDigitDisplay,
|
|
10
|
+
table,
|
|
11
|
+
spawnIOTmp,
|
|
12
|
+
setShowSpinner,
|
|
13
|
+
getShowSpinner,
|
|
14
|
+
} from '../common.js';
|
|
15
|
+
import ora from '#ora';
|
|
16
16
|
|
|
17
17
|
export async function opt(componentPath, opts, program) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
await $init;
|
|
19
|
+
const varIdx = program.parent.rawArgs.indexOf('--');
|
|
20
|
+
if (varIdx !== -1) opts.optArgs = program.parent.rawArgs.slice(varIdx + 1);
|
|
21
|
+
const componentBytes = await readFile(componentPath);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (!opts.quiet) setShowSpinner(true);
|
|
24
|
+
const optPromise = optimizeComponent(componentBytes, opts);
|
|
25
|
+
const { component, compressionInfo } = await optPromise;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
await writeFile(opts.output, component);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
let totalBeforeBytes = 0,
|
|
30
|
+
totalAfterBytes = 0;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (!opts.quiet)
|
|
33
|
+
console.log(c`
|
|
34
34
|
{bold Optimized WebAssembly Component Internal Core Modules:}
|
|
35
35
|
|
|
36
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
37
|
[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
],
|
|
56
57
|
],
|
|
57
|
-
|
|
58
|
-
[, , , , "right"]
|
|
58
|
+
[, , , , 'right']
|
|
59
59
|
)}`);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -65,12 +65,12 @@ ${table(
|
|
|
65
65
|
* @returns {number}
|
|
66
66
|
*/
|
|
67
67
|
function byteLengthLEB128(val) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
let len = 0;
|
|
69
|
+
do {
|
|
70
|
+
val >>>= 7;
|
|
71
|
+
len++;
|
|
72
|
+
} while (val !== 0);
|
|
73
|
+
return len;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -80,165 +80,190 @@ function byteLengthLEB128(val) {
|
|
|
80
80
|
* @returns {Promise<{ component: Uint8Array, compressionInfo: { beforeBytes: number, afterBytes: number }[] >}
|
|
81
81
|
*/
|
|
82
82
|
export async function optimizeComponent(componentBytes, opts) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
await $init;
|
|
84
|
+
const showSpinner = getShowSpinner();
|
|
85
|
+
let spinner;
|
|
86
|
+
try {
|
|
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.filter(
|
|
96
|
+
({ metaType }) => metaType.tag === 'module'
|
|
97
|
+
);
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
// log number of core Wasm modules to be run with wasm-opt
|
|
100
|
+
let completed = 0;
|
|
101
|
+
const spinnerText = () =>
|
|
102
|
+
c`{cyan ${completed} / ${coreModules.length}} Running Binaryen on WebAssembly Component Internal Core Modules \n`;
|
|
103
|
+
if (showSpinner) {
|
|
104
|
+
spinner = ora({
|
|
105
|
+
color: 'cyan',
|
|
106
|
+
spinner: 'bouncingBar',
|
|
107
|
+
}).start();
|
|
108
|
+
spinner.text = spinnerText();
|
|
109
|
+
}
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
// gather the options for wasm-opt. optionally, adding the asyncify flag
|
|
112
|
+
const args = opts?.optArgs
|
|
113
|
+
? [...opts.optArgs]
|
|
114
|
+
: [
|
|
115
|
+
'-Oz',
|
|
116
|
+
'--low-memory-unused',
|
|
117
|
+
'--enable-bulk-memory',
|
|
118
|
+
'--strip-debug',
|
|
119
|
+
];
|
|
120
|
+
if (opts?.asyncify) args.push('--asyncify');
|
|
115
121
|
|
|
122
|
+
// process core Wasm modules with wasm-opt
|
|
123
|
+
await Promise.all(
|
|
124
|
+
coreModules.map(async (metadata) => {
|
|
125
|
+
if (metadata.metaType.tag === 'module') {
|
|
126
|
+
// store the wasm-opt processed module in the metadata
|
|
127
|
+
metadata.optimized = await wasmOpt(
|
|
128
|
+
componentBytes.subarray(
|
|
129
|
+
metadata.range[0],
|
|
130
|
+
metadata.range[1]
|
|
131
|
+
),
|
|
132
|
+
args
|
|
133
|
+
);
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
args);
|
|
135
|
+
// compute the size change, including the change to
|
|
136
|
+
// the LEB128 encoding of the size change
|
|
137
|
+
const prevModuleSize =
|
|
138
|
+
metadata.range[1] - metadata.range[0];
|
|
139
|
+
const newModuleSize = metadata.optimized.byteLength;
|
|
140
|
+
metadata.newLEBLen = byteLengthLEB128(newModuleSize);
|
|
141
|
+
metadata.sizeChange = newModuleSize - prevModuleSize;
|
|
125
142
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
143
|
+
if (spinner) {
|
|
144
|
+
completed++;
|
|
145
|
+
spinner.text = spinnerText();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
);
|
|
132
150
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
// organize components in modules into tree parent and children
|
|
152
|
+
const nodes = componentMetadata.slice(1);
|
|
153
|
+
const getChildren = (parentIndex) => {
|
|
154
|
+
const children = [];
|
|
155
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
156
|
+
const metadata = nodes[i];
|
|
157
|
+
if (metadata.parentIndex === parentIndex) {
|
|
158
|
+
nodes.splice(i, 1); // remove from nodes
|
|
159
|
+
i--;
|
|
160
|
+
metadata.children = getChildren(metadata.index);
|
|
161
|
+
metadata.sizeChange = metadata.children.reduce(
|
|
162
|
+
(total, { prevLEBLen, newLEBLen, sizeChange }) => {
|
|
163
|
+
return sizeChange
|
|
164
|
+
? total + sizeChange + newLEBLen - prevLEBLen
|
|
165
|
+
: total;
|
|
166
|
+
},
|
|
167
|
+
metadata.sizeChange || 0
|
|
168
|
+
);
|
|
169
|
+
const prevSize = metadata.range[1] - metadata.range[0];
|
|
170
|
+
metadata.newLEBLen = byteLengthLEB128(
|
|
171
|
+
prevSize + metadata.sizeChange
|
|
172
|
+
);
|
|
173
|
+
children.push(metadata);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return children;
|
|
177
|
+
};
|
|
178
|
+
const componentTree = getChildren(0);
|
|
139
179
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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;
|
|
180
|
+
// compute the total size change in the component binary
|
|
181
|
+
const sizeChange = componentTree.reduce(
|
|
182
|
+
(total, { prevLEBLen, newLEBLen, sizeChange }) => {
|
|
183
|
+
return total + (sizeChange || 0) + newLEBLen - prevLEBLen;
|
|
155
184
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const componentTree = getChildren(0);
|
|
185
|
+
0
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
let outComponentBytes = new Uint8Array(
|
|
189
|
+
componentBytes.byteLength + sizeChange
|
|
190
|
+
);
|
|
191
|
+
let nextReadPos = 0,
|
|
192
|
+
nextWritePos = 0;
|
|
165
193
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
194
|
+
const write = ({
|
|
195
|
+
prevLEBLen,
|
|
196
|
+
range,
|
|
197
|
+
optimized,
|
|
198
|
+
children,
|
|
199
|
+
sizeChange,
|
|
200
|
+
}) => {
|
|
201
|
+
// write from the last read to the LEB byte start
|
|
202
|
+
outComponentBytes.set(
|
|
203
|
+
componentBytes.subarray(nextReadPos, range[0] - prevLEBLen),
|
|
204
|
+
nextWritePos
|
|
205
|
+
);
|
|
206
|
+
nextWritePos += range[0] - prevLEBLen - nextReadPos;
|
|
171
207
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
208
|
+
// write the new LEB bytes
|
|
209
|
+
let val = range[1] - range[0] + sizeChange;
|
|
210
|
+
do {
|
|
211
|
+
const byte = val & 0x7f;
|
|
212
|
+
val >>>= 7;
|
|
213
|
+
outComponentBytes[nextWritePos++] =
|
|
214
|
+
val === 0 ? byte : byte | 0x80;
|
|
215
|
+
} while (val !== 0);
|
|
175
216
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
217
|
+
if (optimized) {
|
|
218
|
+
// write the core module
|
|
219
|
+
outComponentBytes.set(optimized, nextWritePos);
|
|
220
|
+
nextReadPos = range[1];
|
|
221
|
+
nextWritePos += optimized.byteLength;
|
|
222
|
+
} else if (children.length > 0) {
|
|
223
|
+
// write child components / modules
|
|
224
|
+
nextReadPos = range[0];
|
|
225
|
+
children.forEach(write);
|
|
226
|
+
} else {
|
|
227
|
+
// write component
|
|
228
|
+
outComponentBytes.set(
|
|
229
|
+
componentBytes.subarray(range[0], range[1]),
|
|
230
|
+
nextWritePos
|
|
231
|
+
);
|
|
232
|
+
nextReadPos = range[1];
|
|
233
|
+
nextWritePos += range[1] - range[0];
|
|
234
|
+
}
|
|
235
|
+
};
|
|
183
236
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
do {
|
|
187
|
-
const byte = val & 0x7f;
|
|
188
|
-
val >>>= 7;
|
|
189
|
-
outComponentBytes[nextWritePos++] = val === 0 ? byte : byte | 0x80;
|
|
190
|
-
} while (val !== 0);
|
|
237
|
+
// write each top-level component / module
|
|
238
|
+
componentTree.forEach(write);
|
|
191
239
|
|
|
192
|
-
|
|
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
|
|
240
|
+
// write remaining
|
|
203
241
|
outComponentBytes.set(
|
|
204
|
-
|
|
205
|
-
|
|
242
|
+
componentBytes.subarray(nextReadPos),
|
|
243
|
+
nextWritePos
|
|
206
244
|
);
|
|
207
|
-
nextReadPos = range[1];
|
|
208
|
-
nextWritePos += range[1] - range[0];
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// write each top-level component / module
|
|
213
|
-
componentTree.forEach(write);
|
|
214
245
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
246
|
+
// verify it still parses ok
|
|
247
|
+
if (!opts?.noVerify) {
|
|
248
|
+
try {
|
|
249
|
+
await print(outComponentBytes);
|
|
250
|
+
} catch (e) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Internal error performing optimization.\n${e.message}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
220
256
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
257
|
+
return {
|
|
258
|
+
component: outComponentBytes,
|
|
259
|
+
compressionInfo: coreModules.map(({ range, optimized }) => ({
|
|
260
|
+
beforeBytes: range[1] - range[0],
|
|
261
|
+
afterBytes: optimized?.byteLength,
|
|
262
|
+
})),
|
|
263
|
+
};
|
|
264
|
+
} finally {
|
|
265
|
+
if (spinner) spinner.stop();
|
|
230
266
|
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
component: outComponentBytes,
|
|
234
|
-
compressionInfo: coreModules.map(({range, optimized}) => ({
|
|
235
|
-
beforeBytes: range[1] - range[0],
|
|
236
|
-
afterBytes: optimized?.byteLength,
|
|
237
|
-
})),
|
|
238
|
-
};
|
|
239
|
-
} finally {
|
|
240
|
-
if (spinner) spinner.stop();
|
|
241
|
-
}
|
|
242
267
|
}
|
|
243
268
|
|
|
244
269
|
/**
|
|
@@ -247,15 +272,21 @@ export async function optimizeComponent(componentBytes, opts) {
|
|
|
247
272
|
* @returns {Promise<Uint8Array>}
|
|
248
273
|
*/
|
|
249
274
|
async function wasmOpt(source, args) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
275
|
+
const wasmOptPath = fileURLToPath(
|
|
276
|
+
import.meta.resolve('binaryen/bin/wasm-opt')
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
return await spawnIOTmp(wasmOptPath, source, [...args, '-o']);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
if (e.toString().includes('BasicBlock requested'))
|
|
283
|
+
return wasmOpt(source, args);
|
|
284
|
+
throw e;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
253
287
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return wasmOpt(source, args);
|
|
259
|
-
throw e;
|
|
260
|
-
}
|
|
288
|
+
// see: https://github.com/vitest-dev/vitest/issues/6953#issuecomment-2505310022
|
|
289
|
+
if (typeof __vite_ssr_import_meta__ !== 'undefined') {
|
|
290
|
+
__vite_ssr_import_meta__.resolve = (path) =>
|
|
291
|
+
'file://' + globalCreateRequire(import.meta.url).resolve(path);
|
|
261
292
|
}
|