watr 4.6.10 → 4.7.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/dist/watr.js +1138 -383
- package/dist/watr.min.js +6 -6
- package/dist/watr.wasm +0 -0
- package/package.json +8 -4
- package/src/compile.js +93 -26
- package/src/optimize.js +1359 -394
- package/types/src/compile.d.ts +5 -1
- package/types/src/compile.d.ts.map +1 -1
- package/types/src/optimize.d.ts +28 -46
- package/types/src/optimize.d.ts.map +1 -1
package/dist/watr.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "watr",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Light & fast WAT compiler – WebAssembly Text to binary, parse, print, transform",
|
|
5
5
|
"main": "watr.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"./compile": {
|
|
27
27
|
"types": "./types/src/compile.d.ts",
|
|
28
28
|
"default": "./src/compile.js"
|
|
29
|
+
},
|
|
30
|
+
"./optimize": {
|
|
31
|
+
"types": "./types/src/optimize.d.ts",
|
|
32
|
+
"default": "./src/optimize.js"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"typesVersions": {
|
|
@@ -45,12 +49,12 @@
|
|
|
45
49
|
"scripts": {
|
|
46
50
|
"build": "npm run build:js && npm run build:wasm",
|
|
47
51
|
"build:js": "esbuild watr.js --bundle --format=esm --outfile=dist/watr.js && esbuild watr.js --bundle --format=esm --minify --outfile=dist/watr.min.js",
|
|
48
|
-
"build:wasm": "npx jz watr.js -
|
|
52
|
+
"build:wasm": "npx jz watr.js -O3 -o dist/watr.wasm",
|
|
49
53
|
"test": "node test",
|
|
50
54
|
"test:wasm": "WATR_WASM=1 node test",
|
|
51
55
|
"test:repl": "npx playwright test",
|
|
52
56
|
"types": "npx tsc src/*.js watr.js --allowJs --declaration --emitDeclarationOnly --declarationMap --outDir types",
|
|
53
|
-
"prepublishOnly": "npm
|
|
57
|
+
"prepublishOnly": "npm run build && npm run types && npm test && npm run test:wasm",
|
|
54
58
|
"prepare": "git submodule update --init --recursive 2>/dev/null || true"
|
|
55
59
|
},
|
|
56
60
|
"repository": {
|
|
@@ -93,7 +97,7 @@
|
|
|
93
97
|
"devDependencies": {
|
|
94
98
|
"@playwright/test": "^1.57.0",
|
|
95
99
|
"esbuild": "^0.24.2",
|
|
96
|
-
"jz": "^0.
|
|
100
|
+
"jz": "^0.7.0",
|
|
97
101
|
"tst": "^9.0.0"
|
|
98
102
|
}
|
|
99
103
|
}
|
package/src/compile.js
CHANGED
|
@@ -41,7 +41,11 @@ const cleanup = (node, result) => {
|
|
|
41
41
|
* Converts a WebAssembly Text Format (WAT) tree to a WebAssembly binary format (WASM).
|
|
42
42
|
*
|
|
43
43
|
* @param {string|Array} nodes - The WAT tree or string to be compiled to WASM binary.
|
|
44
|
-
* @returns {Uint8Array} The compiled WASM binary
|
|
44
|
+
* @returns {Uint8Array} The compiled WASM binary. When the WAT carries
|
|
45
|
+
* `@metadata.code.<type>` annotations, the result also exposes a `.metadata`
|
|
46
|
+
* property — `{ [type]: [[absoluteByteOffset, data], ...] }`, each offset the
|
|
47
|
+
* instruction's position in the final binary. That's the hook a source-map
|
|
48
|
+
* emitter needs to correlate generated wasm back to its source.
|
|
45
49
|
*/
|
|
46
50
|
export default function compile(nodes) {
|
|
47
51
|
// normalize to (module ...) form
|
|
@@ -237,32 +241,80 @@ export default function compile(nodes) {
|
|
|
237
241
|
// pre-compute sections that may collect string constants (for strings section ordering)
|
|
238
242
|
const globalSection = bin(SECTION.global)
|
|
239
243
|
const elemSection = bin(SECTION.elem)
|
|
240
|
-
|
|
244
|
+
// inline bin(code) so the per-function item bytes survive — with ctx.codeSizePrefix
|
|
245
|
+
// they let us lift code-metadata positions to absolute binary offsets below
|
|
246
|
+
const codeItems = ctx.code.filter(Boolean).map(item => build[SECTION.code](item, ctx)).filter(Boolean)
|
|
247
|
+
const codeSection = codeItems.length ? [SECTION.code, ...vec(vec(codeItems))] : []
|
|
241
248
|
const metaSection = binMeta()
|
|
242
249
|
const dataSection = bin(SECTION.data)
|
|
243
250
|
const stringsSection = ctx.strings.length ? [SECTION.strings, ...vec([0x00, ...vec(ctx.strings.map(s => vec(s)))])] : []
|
|
244
251
|
|
|
252
|
+
// sections in binary order (each a byte array — lengths feed offset math)
|
|
253
|
+
const sections = [
|
|
254
|
+
bin(SECTION.custom),
|
|
255
|
+
bin(SECTION.type),
|
|
256
|
+
bin(SECTION.import),
|
|
257
|
+
bin(SECTION.func),
|
|
258
|
+
bin(SECTION.table),
|
|
259
|
+
bin(SECTION.memory),
|
|
260
|
+
bin(SECTION.tag),
|
|
261
|
+
stringsSection,
|
|
262
|
+
globalSection,
|
|
263
|
+
bin(SECTION.export),
|
|
264
|
+
bin(SECTION.start, false),
|
|
265
|
+
elemSection,
|
|
266
|
+
bin(SECTION.datacount, false),
|
|
267
|
+
codeSection,
|
|
268
|
+
metaSection,
|
|
269
|
+
dataSection
|
|
270
|
+
]
|
|
271
|
+
|
|
245
272
|
// build final binary
|
|
246
|
-
|
|
273
|
+
const wasm = Uint8Array.from([
|
|
247
274
|
0x00, 0x61, 0x73, 0x6d, // magic
|
|
248
275
|
0x01, 0x00, 0x00, 0x00, // version
|
|
249
|
-
...
|
|
250
|
-
...bin(SECTION.type),
|
|
251
|
-
...bin(SECTION.import),
|
|
252
|
-
...bin(SECTION.func),
|
|
253
|
-
...bin(SECTION.table),
|
|
254
|
-
...bin(SECTION.memory),
|
|
255
|
-
...bin(SECTION.tag),
|
|
256
|
-
...stringsSection,
|
|
257
|
-
...globalSection,
|
|
258
|
-
...bin(SECTION.export),
|
|
259
|
-
...bin(SECTION.start, false),
|
|
260
|
-
...elemSection,
|
|
261
|
-
...bin(SECTION.datacount, false),
|
|
262
|
-
...codeSection,
|
|
263
|
-
...metaSection,
|
|
264
|
-
...dataSection
|
|
276
|
+
...sections.flat()
|
|
265
277
|
])
|
|
278
|
+
|
|
279
|
+
// Lift any (@metadata.code.*) annotations to absolute binary offsets and hang
|
|
280
|
+
// them off the result, so a source-map emitter can correlate wasm back to
|
|
281
|
+
// source. The result stays a Uint8Array; .metadata appears only when the WAT
|
|
282
|
+
// actually carried annotations — the common (annotation-free) path stays free.
|
|
283
|
+
const md = metadataOffsets()
|
|
284
|
+
if (md) wasm.metadata = md
|
|
285
|
+
|
|
286
|
+
return wasm
|
|
287
|
+
|
|
288
|
+
// Map each recorded code-metadata position (function-body-relative, as stored
|
|
289
|
+
// by build.code) to its absolute byte offset in the final binary.
|
|
290
|
+
// → { [type]: [[absoluteByteOffset, data], ...] } sorted by offset, or
|
|
291
|
+
// undefined when no annotations were present.
|
|
292
|
+
function metadataOffsets() {
|
|
293
|
+
let has = false
|
|
294
|
+
for (const _ in ctx.metadata) has = true // any annotations? (no break — jz self-host lowers for-in without one)
|
|
295
|
+
if (!has) return
|
|
296
|
+
const out = {}
|
|
297
|
+
// 8 = magic + version; sections before code give the code section's base
|
|
298
|
+
const codeBase = 8 + sections.slice(0, sections.indexOf(codeSection)).reduce((n, s) => n + s.length, 0)
|
|
299
|
+
// function items are the tail of the code section (after id + size + count)
|
|
300
|
+
const itemsBase = codeBase + codeSection.length - codeItems.reduce((n, it) => n + it.length, 0)
|
|
301
|
+
// per code index → absolute offset of its function body (start of locals vec)
|
|
302
|
+
const bodyBase = []
|
|
303
|
+
for (let i = 0, off = itemsBase; i < codeItems.length; i++) {
|
|
304
|
+
bodyBase[i] = off + (ctx.codeSizePrefix?.[i] ?? 0)
|
|
305
|
+
off += codeItems[i].length
|
|
306
|
+
}
|
|
307
|
+
const importedFuncs = ctx.import.filter(imp => imp[2][0] === 'func').length
|
|
308
|
+
for (const type in ctx.metadata) {
|
|
309
|
+
const entries = []
|
|
310
|
+
for (const [funcIdx, instances] of ctx.metadata[type]) {
|
|
311
|
+
const base = bodyBase[funcIdx - importedFuncs]
|
|
312
|
+
for (const [pos, data] of instances) entries.push([base + pos, data])
|
|
313
|
+
}
|
|
314
|
+
out[type] = entries.sort((a, b) => a[0] - b[0])
|
|
315
|
+
}
|
|
316
|
+
return out
|
|
317
|
+
}
|
|
266
318
|
}
|
|
267
319
|
|
|
268
320
|
|
|
@@ -722,19 +774,30 @@ const build = [
|
|
|
722
774
|
ctx.meta = {}
|
|
723
775
|
const bytes = instr(body, ctx)
|
|
724
776
|
|
|
725
|
-
// Store collected metadata for this function
|
|
726
|
-
const funcIdx = ctx.import.filter(imp => imp[2][0] === 'func').length + codeIdx
|
|
727
|
-
for (const type in ctx.meta) ((ctx.metadata ??= {})[type] ??= []).push([funcIdx, ctx.meta[type]])
|
|
728
|
-
|
|
729
777
|
// squash locals into (n:u32 t:valtype)*, n is number and t is type
|
|
730
778
|
// we skip locals provided by params
|
|
731
779
|
let loctypes = ctx.local.slice(param.length).reduce((a, type) => (type == a[a.length - 1]?.[1] ? a[a.length - 1][0]++ : a.push([1, type]), a), [])
|
|
780
|
+
const locals = vec(loctypes.map(([n, t]) => [...uleb(n), ...reftype(t, ctx)]))
|
|
781
|
+
|
|
782
|
+
// Store collected metadata. instr() records positions relative to the
|
|
783
|
+
// instruction stream; shift them past the locals vec so they are
|
|
784
|
+
// function-body-relative — the reference point the code-metadata spec (and
|
|
785
|
+
// wabt/binaryen) use for the metadata.code.* sections.
|
|
786
|
+
const funcIdx = ctx.import.filter(imp => imp[2][0] === 'func').length + codeIdx
|
|
787
|
+
for (const type in ctx.meta) {
|
|
788
|
+
for (const inst of ctx.meta[type]) inst[0] += locals.length
|
|
789
|
+
;((ctx.metadata ??= {})[type] ??= []).push([funcIdx, ctx.meta[type]])
|
|
790
|
+
}
|
|
732
791
|
|
|
733
792
|
// cleanup tmp state
|
|
734
793
|
ctx.local = ctx.block = ctx.meta = null
|
|
735
794
|
|
|
736
795
|
// https://webassembly.github.io/spec/core/binary/modules.html#code-section
|
|
737
|
-
|
|
796
|
+
const item = vec([...locals, ...bytes])
|
|
797
|
+
// size-prefix length (item minus its body) lets the now function-body-relative
|
|
798
|
+
// metadata positions be lifted to absolute binary offsets
|
|
799
|
+
;(ctx.codeSizePrefix ??= [])[codeIdx] = item.length - locals.length - bytes.length
|
|
800
|
+
return item
|
|
738
801
|
},
|
|
739
802
|
|
|
740
803
|
// (data (i32.const 0) "\aa" "\bb"?)
|
|
@@ -986,8 +1049,12 @@ const instr = (nodes, ctx) => {
|
|
|
986
1049
|
bytes.push(...HANDLER[op](nodes, ctx, op))
|
|
987
1050
|
}
|
|
988
1051
|
|
|
989
|
-
//
|
|
990
|
-
|
|
1052
|
+
// Attach any pending annotations to this instruction's byte position, then
|
|
1053
|
+
// clear — a (@metadata.code.*) annotation applies to the next instruction only
|
|
1054
|
+
if (meta.length) {
|
|
1055
|
+
for (const [type, data] of meta) ((ctx.meta[type] ??= []).push([out.length, data]))
|
|
1056
|
+
meta = []
|
|
1057
|
+
}
|
|
991
1058
|
|
|
992
1059
|
out.push(...bytes)
|
|
993
1060
|
}
|