watr 4.5.1 → 4.5.3
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 +134 -99
- package/dist/watr.min.js +6 -6
- package/package.json +1 -1
- package/src/compile.js +9 -2
- package/src/const.js +2 -2
- package/src/encode.js +33 -12
- package/src/optimize.js +119 -85
- package/types/src/const.d.ts +2 -0
- package/types/src/encode.d.ts.map +1 -1
- package/types/src/optimize.d.ts +13 -0
- package/types/src/optimize.d.ts.map +1 -1
package/dist/watr.js
CHANGED
|
@@ -140,16 +140,29 @@ var _f64 = new Float64Array(_buf);
|
|
|
140
140
|
var _i64 = new BigInt64Array(_buf);
|
|
141
141
|
i64.parse = (n) => {
|
|
142
142
|
n = cleanInt(n);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
const neg = n[0] === "-";
|
|
144
|
+
const body = neg ? n.slice(1) : n;
|
|
145
|
+
let max;
|
|
146
|
+
if (body[0] === "0" && (body[1] === "x" || body[1] === "X")) {
|
|
147
|
+
const hex = body.slice(2).replace(/^0+/, "") || "0";
|
|
148
|
+
max = neg ? "8000000000000000" : "ffffffffffffffff";
|
|
149
|
+
if (hex.length > 16 || hex.length === 16 && hex.toLowerCase() > max) err(`i64 constant out of range`);
|
|
150
|
+
} else {
|
|
151
|
+
const dec = body.replace(/^0+/, "") || "0";
|
|
152
|
+
max = neg ? "9223372036854775808" : "18446744073709551615";
|
|
153
|
+
if (dec.length > max.length || dec.length === max.length && dec > max) err(`i64 constant out of range`);
|
|
154
|
+
}
|
|
155
|
+
let bi = BigInt(body);
|
|
156
|
+
if (neg) bi = 0n - bi;
|
|
157
|
+
_i64[0] = bi;
|
|
146
158
|
return _i64[0];
|
|
147
159
|
};
|
|
148
160
|
var F32_SIGN = 2147483648;
|
|
149
161
|
var F32_NAN = 2139095040;
|
|
162
|
+
var F32_QUIET = 4194304;
|
|
150
163
|
function f32(input, value, idx) {
|
|
151
|
-
if (typeof input === "string" &&
|
|
152
|
-
value = i32.parse(input.slice(idx + 4));
|
|
164
|
+
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
|
|
165
|
+
value = input[idx + 3] === ":" ? i32.parse(input.slice(idx + 4)) : F32_QUIET;
|
|
153
166
|
value |= F32_NAN;
|
|
154
167
|
if (input[0] === "-") value |= F32_SIGN;
|
|
155
168
|
_i32[0] = value;
|
|
@@ -161,9 +174,10 @@ function f32(input, value, idx) {
|
|
|
161
174
|
}
|
|
162
175
|
var F64_SIGN = 0x8000000000000000n;
|
|
163
176
|
var F64_NAN = 0x7ff0000000000000n;
|
|
177
|
+
var F64_QUIET = 0x8000000000000n;
|
|
164
178
|
function f64(input, value, idx) {
|
|
165
|
-
if (typeof input === "string" &&
|
|
166
|
-
value = i64.parse(input.slice(idx + 4));
|
|
179
|
+
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
|
|
180
|
+
value = input[idx + 3] === ":" ? i64.parse(input.slice(idx + 4)) : F64_QUIET;
|
|
167
181
|
value |= F64_NAN;
|
|
168
182
|
if (input[0] === "-") value |= F64_SIGN;
|
|
169
183
|
_i64[0] = value;
|
|
@@ -182,8 +196,11 @@ f64.parse = (input, max = Number.MAX_VALUE) => {
|
|
|
182
196
|
let [sig, exp = "0"] = input.split(/p/i);
|
|
183
197
|
let [int, fract = ""] = sig.split(".");
|
|
184
198
|
let flen = fract.length ?? 0;
|
|
185
|
-
let intVal =
|
|
186
|
-
|
|
199
|
+
let intVal = 0;
|
|
200
|
+
for (let i = int.length - 1; i >= 2; i--) {
|
|
201
|
+
let digit = parseInt(int[i], 16);
|
|
202
|
+
intVal += digit * 16 ** (int.length - 1 - i);
|
|
203
|
+
}
|
|
187
204
|
let fractVal = fract ? parseInt("0x" + fract) / 16 ** flen : 0;
|
|
188
205
|
exp = parseInt(exp, 10);
|
|
189
206
|
let value = sign * (intVal + fractVal) * 2 ** exp;
|
|
@@ -497,9 +514,9 @@ var INSTR = [
|
|
|
497
514
|
"array.init_data typeidx_dataidx",
|
|
498
515
|
"array.init_elem typeidx_elemidx",
|
|
499
516
|
"ref.test reftype",
|
|
500
|
-
"",
|
|
517
|
+
"ref.test_null reftype",
|
|
501
518
|
"ref.cast reftype",
|
|
502
|
-
"",
|
|
519
|
+
"ref.cast_null reftype",
|
|
503
520
|
"br_on_cast reftype2",
|
|
504
521
|
"br_on_cast_fail reftype2",
|
|
505
522
|
"any.convert_extern",
|
|
@@ -1244,6 +1261,7 @@ var TYPE = {
|
|
|
1244
1261
|
i31: 108,
|
|
1245
1262
|
struct: 107,
|
|
1246
1263
|
array: 106,
|
|
1264
|
+
data: 107,
|
|
1247
1265
|
cont: 104,
|
|
1248
1266
|
nocont: 117,
|
|
1249
1267
|
// stack switching (Phase 3)
|
|
@@ -1441,10 +1459,10 @@ function compile(nodes) {
|
|
|
1441
1459
|
if (imported) ctx.import.push([...imported, [kind, ...node]]), node = null;
|
|
1442
1460
|
items.push(node);
|
|
1443
1461
|
});
|
|
1444
|
-
const bin = (kind,
|
|
1462
|
+
const bin = (kind, count2 = true) => {
|
|
1445
1463
|
const items = ctx[kind].filter(Boolean).map((item) => build[kind](item, ctx)).filter(Boolean);
|
|
1446
1464
|
if (kind === SECTION.custom) return items.flatMap((content) => [kind, ...vec(content)]);
|
|
1447
|
-
return !items.length ? [] : [kind, ...vec(
|
|
1465
|
+
return !items.length ? [] : [kind, ...vec(count2 ? vec(items) : items.flat())];
|
|
1448
1466
|
};
|
|
1449
1467
|
const binMeta = () => {
|
|
1450
1468
|
const sections = [];
|
|
@@ -1522,7 +1540,7 @@ function normalize(nodes, ctx) {
|
|
|
1522
1540
|
ctx.datacount && (ctx.datacount[0] = true);
|
|
1523
1541
|
} else if ((node.startsWith("memory.") || node.endsWith("load") || node.endsWith("store")) && isIdx(nodes[0])) out.push(nodes.shift());
|
|
1524
1542
|
} else if (Array.isArray(node)) {
|
|
1525
|
-
|
|
1543
|
+
let op = node[0];
|
|
1526
1544
|
node.loc != null && (err.loc = node.loc);
|
|
1527
1545
|
if (op?.startsWith?.("@metadata.code.")) {
|
|
1528
1546
|
let type = op.slice(15);
|
|
@@ -1556,6 +1574,12 @@ function normalize(nodes, ctx) {
|
|
|
1556
1574
|
out.push(parts.shift());
|
|
1557
1575
|
}
|
|
1558
1576
|
out.push(...normalize(parts, ctx), "end");
|
|
1577
|
+
} else if (op === "ref.test" || op === "ref.cast") {
|
|
1578
|
+
const type = parts[0];
|
|
1579
|
+
const isNullable = !Array.isArray(type) || type[1] === "null" || type[0] !== "ref";
|
|
1580
|
+
if (isNullable) op += "_null";
|
|
1581
|
+
out.push(...normalize(parts.slice(1), ctx), op, type);
|
|
1582
|
+
nodes.unshift(...out.splice(out.length - 2));
|
|
1559
1583
|
} else {
|
|
1560
1584
|
const imm = [];
|
|
1561
1585
|
while (parts.length && (!Array.isArray(parts[0]) || parts[0].valueOf !== Array.prototype.valueOf || "type,param,result,ref,exact,on".includes(parts[0][0]))) imm.push(parts.shift());
|
|
@@ -1845,16 +1869,16 @@ var IMM = {
|
|
|
1845
1869
|
isId(n[0]) && (c.block[n.shift()] = c.block.length + 1);
|
|
1846
1870
|
let blocktype2 = n.shift();
|
|
1847
1871
|
let result = !blocktype2 ? [TYPE.void] : blocktype2[0] === "result" ? reftype(blocktype2[1], c) : uleb(id(blocktype2[1], c.type));
|
|
1848
|
-
let catches = [],
|
|
1872
|
+
let catches = [], count2 = 0;
|
|
1849
1873
|
while (n[0]?.[0] === "catch" || n[0]?.[0] === "catch_ref" || n[0]?.[0] === "catch_all" || n[0]?.[0] === "catch_all_ref") {
|
|
1850
1874
|
let clause = n.shift();
|
|
1851
1875
|
let kind = clause[0] === "catch" ? 0 : clause[0] === "catch_ref" ? 1 : clause[0] === "catch_all" ? 2 : 3;
|
|
1852
1876
|
if (kind <= 1) catches.push(kind, ...uleb(id(clause[1], c.tag)), ...uleb(blockid(clause[2], c.block)));
|
|
1853
1877
|
else catches.push(kind, ...uleb(blockid(clause[1], c.block)));
|
|
1854
|
-
|
|
1878
|
+
count2++;
|
|
1855
1879
|
}
|
|
1856
1880
|
c.block.push(1);
|
|
1857
|
-
return [...result, ...uleb(
|
|
1881
|
+
return [...result, ...uleb(count2), ...catches];
|
|
1858
1882
|
},
|
|
1859
1883
|
end: (_n, c) => (c.block.pop(), []),
|
|
1860
1884
|
call_indirect: (n, c) => {
|
|
@@ -1862,9 +1886,9 @@ var IMM = {
|
|
|
1862
1886
|
return [...uleb(id(idx, c.type)), ...uleb(id(t, c.table))];
|
|
1863
1887
|
},
|
|
1864
1888
|
br_table: (n, c) => {
|
|
1865
|
-
let labels = [],
|
|
1866
|
-
while (n[0] && (!isNaN(n[0]) || isId(n[0]))) labels.push(...uleb(blockid(n.shift(), c.block))),
|
|
1867
|
-
return [...uleb(
|
|
1889
|
+
let labels = [], count2 = 0;
|
|
1890
|
+
while (n[0] && (!isNaN(n[0]) || isId(n[0]))) labels.push(...uleb(blockid(n.shift(), c.block))), count2++;
|
|
1891
|
+
return [...uleb(count2 - 1), ...labels];
|
|
1868
1892
|
},
|
|
1869
1893
|
select: (n, c) => {
|
|
1870
1894
|
let r = n.shift() || [];
|
|
@@ -2006,7 +2030,7 @@ var instr = (nodes, ctx) => {
|
|
|
2006
2030
|
let [...bytes] = INSTR[op] || err(`Unknown instruction ${op}`);
|
|
2007
2031
|
if (HANDLER[op]) {
|
|
2008
2032
|
if (op === "select" && nodes[0]?.length) bytes[0]++;
|
|
2009
|
-
else if (HANDLER[op] === IMM.reftype && (nodes[0][1] === "null" || nodes[0][0] !== "ref")) {
|
|
2033
|
+
else if (HANDLER[op] === IMM.reftype && !op.endsWith("_null") && (nodes[0][1] === "null" || nodes[0][0] !== "ref")) {
|
|
2010
2034
|
bytes[bytes.length - 1]++;
|
|
2011
2035
|
}
|
|
2012
2036
|
bytes.push(...HANDLER[op](nodes, ctx, op));
|
|
@@ -3040,10 +3064,10 @@ var OPTS = {
|
|
|
3040
3064
|
// strength reduction (x * 2 → x << 1)
|
|
3041
3065
|
branch: true,
|
|
3042
3066
|
// simplify constant branches
|
|
3043
|
-
propagate:
|
|
3044
|
-
// constant propagation
|
|
3045
|
-
inline:
|
|
3046
|
-
// inline tiny functions
|
|
3067
|
+
propagate: false,
|
|
3068
|
+
// constant propagation — can duplicate expressions
|
|
3069
|
+
inline: false,
|
|
3070
|
+
// inline tiny functions — can duplicate bodies
|
|
3047
3071
|
vacuum: true,
|
|
3048
3072
|
// remove nops, drop-of-pure, empty branches
|
|
3049
3073
|
peephole: true,
|
|
@@ -3058,14 +3082,12 @@ var OPTS = {
|
|
|
3058
3082
|
// strip mut from never-written globals
|
|
3059
3083
|
brif: true,
|
|
3060
3084
|
// if-then-br → br_if
|
|
3061
|
-
foldarms:
|
|
3062
|
-
// merge identical trailing if arms
|
|
3063
|
-
// minify: true, // NOTE: disabled — renaming $ids has no binary-size effect
|
|
3064
|
-
// without a names section, and risks local-name collisions.
|
|
3085
|
+
foldarms: false,
|
|
3086
|
+
// merge identical trailing if arms — can add block wrapper
|
|
3065
3087
|
dedupe: true,
|
|
3066
3088
|
// eliminate duplicate functions
|
|
3067
|
-
reorder:
|
|
3068
|
-
// put hot functions first
|
|
3089
|
+
reorder: false,
|
|
3090
|
+
// put hot functions first — no AST reduction
|
|
3069
3091
|
dedupTypes: true,
|
|
3070
3092
|
// merge identical type definitions
|
|
3071
3093
|
packData: true,
|
|
@@ -3074,6 +3096,12 @@ var OPTS = {
|
|
|
3074
3096
|
// shorten import names — enable only when you control the host
|
|
3075
3097
|
};
|
|
3076
3098
|
var ALL2 = Object.keys(OPTS);
|
|
3099
|
+
var count = (node) => {
|
|
3100
|
+
if (!Array.isArray(node)) return 1;
|
|
3101
|
+
let n = 1;
|
|
3102
|
+
for (let i = 0; i < node.length; i++) n += count(node[i]);
|
|
3103
|
+
return n;
|
|
3104
|
+
};
|
|
3077
3105
|
var equal = (a, b) => {
|
|
3078
3106
|
if (a === b) return true;
|
|
3079
3107
|
if (typeof a !== typeof b) return false;
|
|
@@ -3088,10 +3116,8 @@ var normalize3 = (opts) => {
|
|
|
3088
3116
|
if (opts === false) return {};
|
|
3089
3117
|
if (typeof opts === "string") {
|
|
3090
3118
|
const set = new Set(opts.split(/\s+/).filter(Boolean));
|
|
3091
|
-
if (set.
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
return Object.fromEntries(ALL2.map((f) => [f, set.has(f) || set.has("all")]));
|
|
3119
|
+
if (set.has("all")) return Object.fromEntries(ALL2.map((f) => [f, true]));
|
|
3120
|
+
return Object.fromEntries(ALL2.map((f) => [f, set.has(f)]));
|
|
3095
3121
|
}
|
|
3096
3122
|
return { ...OPTS, ...opts };
|
|
3097
3123
|
};
|
|
@@ -3401,7 +3427,7 @@ var makeConst = (type, value) => {
|
|
|
3401
3427
|
return null;
|
|
3402
3428
|
};
|
|
3403
3429
|
var fold = (ast) => {
|
|
3404
|
-
return walkPost2(
|
|
3430
|
+
return walkPost2(ast, (node) => {
|
|
3405
3431
|
if (!Array.isArray(node)) return;
|
|
3406
3432
|
const entry = FOLDABLE[node[0]];
|
|
3407
3433
|
if (!entry) return;
|
|
@@ -3464,7 +3490,7 @@ var IDENTITIES = {
|
|
|
3464
3490
|
// f * 1 → x (careful with NaN, skip for floats)
|
|
3465
3491
|
};
|
|
3466
3492
|
var identity = (ast) => {
|
|
3467
|
-
return walkPost2(
|
|
3493
|
+
return walkPost2(ast, (node) => {
|
|
3468
3494
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3469
3495
|
const fn = IDENTITIES[node[0]];
|
|
3470
3496
|
if (!fn) return;
|
|
@@ -3474,7 +3500,7 @@ var identity = (ast) => {
|
|
|
3474
3500
|
});
|
|
3475
3501
|
};
|
|
3476
3502
|
var strength = (ast) => {
|
|
3477
|
-
return walkPost2(
|
|
3503
|
+
return walkPost2(ast, (node) => {
|
|
3478
3504
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3479
3505
|
const [op, a, b] = node;
|
|
3480
3506
|
if (op === "i32.mul") {
|
|
@@ -3530,7 +3556,7 @@ var strength = (ast) => {
|
|
|
3530
3556
|
});
|
|
3531
3557
|
};
|
|
3532
3558
|
var branch = (ast) => {
|
|
3533
|
-
return walkPost2(
|
|
3559
|
+
return walkPost2(ast, (node) => {
|
|
3534
3560
|
if (!Array.isArray(node)) return;
|
|
3535
3561
|
const op = node[0];
|
|
3536
3562
|
if (op === "if") {
|
|
@@ -3562,8 +3588,7 @@ var branch = (ast) => {
|
|
|
3562
3588
|
};
|
|
3563
3589
|
var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
|
|
3564
3590
|
var deadcode = (ast) => {
|
|
3565
|
-
|
|
3566
|
-
walk2(result, (node) => {
|
|
3591
|
+
walk2(ast, (node) => {
|
|
3567
3592
|
if (!Array.isArray(node)) return;
|
|
3568
3593
|
const kind = node[0];
|
|
3569
3594
|
if (kind === "func" || kind === "block" || kind === "loop") {
|
|
@@ -3577,7 +3602,7 @@ var deadcode = (ast) => {
|
|
|
3577
3602
|
}
|
|
3578
3603
|
}
|
|
3579
3604
|
});
|
|
3580
|
-
return
|
|
3605
|
+
return ast;
|
|
3581
3606
|
};
|
|
3582
3607
|
var eliminateDeadInBlock = (block) => {
|
|
3583
3608
|
let terminated = false;
|
|
@@ -3609,8 +3634,7 @@ var eliminateDeadInBlock = (block) => {
|
|
|
3609
3634
|
}
|
|
3610
3635
|
};
|
|
3611
3636
|
var localReuse = (ast) => {
|
|
3612
|
-
|
|
3613
|
-
walk2(result, (node) => {
|
|
3637
|
+
walk2(ast, (node) => {
|
|
3614
3638
|
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
3615
3639
|
const localDecls = [];
|
|
3616
3640
|
const localTypes = /* @__PURE__ */ new Map();
|
|
@@ -3647,7 +3671,7 @@ var localReuse = (ast) => {
|
|
|
3647
3671
|
}
|
|
3648
3672
|
}
|
|
3649
3673
|
});
|
|
3650
|
-
return
|
|
3674
|
+
return ast;
|
|
3651
3675
|
};
|
|
3652
3676
|
var IMPURE_OPS = /* @__PURE__ */ new Set([
|
|
3653
3677
|
"call",
|
|
@@ -3727,7 +3751,7 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3727
3751
|
if (!Array.isArray(instr2)) continue;
|
|
3728
3752
|
const op = instr2[0];
|
|
3729
3753
|
if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
|
|
3730
|
-
if (op === "local.set" && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
3754
|
+
if ((op === "local.set" || op === "local.tee") && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
3731
3755
|
substGets(instr2[2], known);
|
|
3732
3756
|
const uses = getUseCount(instr2[1]);
|
|
3733
3757
|
known.set(instr2[1], {
|
|
@@ -3755,6 +3779,10 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3755
3779
|
const prev = clone2(instr2);
|
|
3756
3780
|
substGets(instr2, known);
|
|
3757
3781
|
if (!equal(prev, instr2)) changed = true;
|
|
3782
|
+
walk2(instr2, (n) => {
|
|
3783
|
+
if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string")
|
|
3784
|
+
known.delete(n[1]);
|
|
3785
|
+
});
|
|
3758
3786
|
}
|
|
3759
3787
|
}
|
|
3760
3788
|
return changed;
|
|
@@ -3813,8 +3841,7 @@ var eliminateDeadStores = (funcNode, params, useCounts) => {
|
|
|
3813
3841
|
return changed;
|
|
3814
3842
|
};
|
|
3815
3843
|
var propagate = (ast) => {
|
|
3816
|
-
|
|
3817
|
-
walk2(result, (funcNode) => {
|
|
3844
|
+
walk2(ast, (funcNode) => {
|
|
3818
3845
|
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
3819
3846
|
const params = /* @__PURE__ */ new Set();
|
|
3820
3847
|
for (const sub of funcNode)
|
|
@@ -3828,13 +3855,12 @@ var propagate = (ast) => {
|
|
|
3828
3855
|
if (!changed) break;
|
|
3829
3856
|
}
|
|
3830
3857
|
});
|
|
3831
|
-
return
|
|
3858
|
+
return ast;
|
|
3832
3859
|
};
|
|
3833
3860
|
var inline = (ast) => {
|
|
3834
3861
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3835
|
-
const result = clone2(ast);
|
|
3836
3862
|
const inlinable = /* @__PURE__ */ new Map();
|
|
3837
|
-
for (const node of
|
|
3863
|
+
for (const node of ast.slice(1)) {
|
|
3838
3864
|
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
3839
3865
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
3840
3866
|
if (!name2) continue;
|
|
@@ -3878,8 +3904,8 @@ var inline = (ast) => {
|
|
|
3878
3904
|
}
|
|
3879
3905
|
}
|
|
3880
3906
|
}
|
|
3881
|
-
if (inlinable.size === 0) return
|
|
3882
|
-
walkPost2(
|
|
3907
|
+
if (inlinable.size === 0) return ast;
|
|
3908
|
+
walkPost2(ast, (node) => {
|
|
3883
3909
|
if (!Array.isArray(node) || node[0] !== "call") return;
|
|
3884
3910
|
const fname = node[1];
|
|
3885
3911
|
if (!inlinable.has(fname)) return;
|
|
@@ -3899,10 +3925,10 @@ var inline = (ast) => {
|
|
|
3899
3925
|
});
|
|
3900
3926
|
return substituted;
|
|
3901
3927
|
});
|
|
3902
|
-
return
|
|
3928
|
+
return ast;
|
|
3903
3929
|
};
|
|
3904
3930
|
var vacuum = (ast) => {
|
|
3905
|
-
return walkPost2(
|
|
3931
|
+
return walkPost2(ast, (node) => {
|
|
3906
3932
|
if (!Array.isArray(node)) return;
|
|
3907
3933
|
const op = node[0];
|
|
3908
3934
|
if (op === "nop") return ["nop"];
|
|
@@ -4001,7 +4027,7 @@ var PEEPHOLE = {
|
|
|
4001
4027
|
"local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
|
|
4002
4028
|
};
|
|
4003
4029
|
var peephole = (ast) => {
|
|
4004
|
-
return walkPost2(
|
|
4030
|
+
return walkPost2(ast, (node) => {
|
|
4005
4031
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
4006
4032
|
const fn = PEEPHOLE[node[0]];
|
|
4007
4033
|
if (!fn) return;
|
|
@@ -4011,10 +4037,9 @@ var peephole = (ast) => {
|
|
|
4011
4037
|
};
|
|
4012
4038
|
var globals = (ast) => {
|
|
4013
4039
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4014
|
-
const result = clone2(ast);
|
|
4015
4040
|
const constGlobals = /* @__PURE__ */ new Map();
|
|
4016
4041
|
const mutableGlobals = /* @__PURE__ */ new Set();
|
|
4017
|
-
for (const node of
|
|
4042
|
+
for (const node of ast.slice(1)) {
|
|
4018
4043
|
if (!Array.isArray(node) || node[0] !== "global") continue;
|
|
4019
4044
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4020
4045
|
if (!name2) continue;
|
|
@@ -4025,21 +4050,21 @@ var globals = (ast) => {
|
|
|
4025
4050
|
const init = node[initIdx];
|
|
4026
4051
|
if (getConst(init)) constGlobals.set(name2, init);
|
|
4027
4052
|
}
|
|
4028
|
-
walk2(
|
|
4053
|
+
walk2(ast, (n) => {
|
|
4029
4054
|
if (!Array.isArray(n) || n[0] !== "global.set") return;
|
|
4030
4055
|
const ref = n[1];
|
|
4031
4056
|
if (typeof ref === "string" && ref[0] === "$") mutableGlobals.add(ref);
|
|
4032
4057
|
});
|
|
4033
4058
|
for (const name2 of mutableGlobals) constGlobals.delete(name2);
|
|
4034
|
-
if (constGlobals.size === 0) return
|
|
4035
|
-
return walkPost2(
|
|
4059
|
+
if (constGlobals.size === 0) return ast;
|
|
4060
|
+
return walkPost2(ast, (node) => {
|
|
4036
4061
|
if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
|
|
4037
4062
|
const ref = node[1];
|
|
4038
4063
|
if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
|
|
4039
4064
|
});
|
|
4040
4065
|
};
|
|
4041
4066
|
var offset = (ast) => {
|
|
4042
|
-
return walkPost2(
|
|
4067
|
+
return walkPost2(ast, (node) => {
|
|
4043
4068
|
if (!Array.isArray(node)) return;
|
|
4044
4069
|
const op = node[0];
|
|
4045
4070
|
if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
|
|
@@ -4090,8 +4115,7 @@ var offset = (ast) => {
|
|
|
4090
4115
|
});
|
|
4091
4116
|
};
|
|
4092
4117
|
var unbranch = (ast) => {
|
|
4093
|
-
|
|
4094
|
-
walk2(result, (node) => {
|
|
4118
|
+
walk2(ast, (node) => {
|
|
4095
4119
|
if (!Array.isArray(node)) return;
|
|
4096
4120
|
const op = node[0];
|
|
4097
4121
|
if (op !== "block") return;
|
|
@@ -4120,16 +4144,15 @@ var unbranch = (ast) => {
|
|
|
4120
4144
|
node.splice(lastIdx, 1);
|
|
4121
4145
|
}
|
|
4122
4146
|
});
|
|
4123
|
-
return
|
|
4147
|
+
return ast;
|
|
4124
4148
|
};
|
|
4125
4149
|
var stripmut = (ast) => {
|
|
4126
4150
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4127
|
-
const result = clone2(ast);
|
|
4128
4151
|
const written = /* @__PURE__ */ new Set();
|
|
4129
|
-
walk2(
|
|
4152
|
+
walk2(ast, (n) => {
|
|
4130
4153
|
if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
|
|
4131
4154
|
});
|
|
4132
|
-
return walkPost2(
|
|
4155
|
+
return walkPost2(ast, (node) => {
|
|
4133
4156
|
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
4134
4157
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4135
4158
|
if (!name2 || written.has(name2)) return;
|
|
@@ -4143,7 +4166,7 @@ var stripmut = (ast) => {
|
|
|
4143
4166
|
});
|
|
4144
4167
|
};
|
|
4145
4168
|
var brif = (ast) => {
|
|
4146
|
-
return walkPost2(
|
|
4169
|
+
return walkPost2(ast, (node) => {
|
|
4147
4170
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4148
4171
|
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
4149
4172
|
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
@@ -4159,7 +4182,7 @@ var brif = (ast) => {
|
|
|
4159
4182
|
});
|
|
4160
4183
|
};
|
|
4161
4184
|
var foldarms = (ast) => {
|
|
4162
|
-
return walkPost2(
|
|
4185
|
+
return walkPost2(ast, (node) => {
|
|
4163
4186
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4164
4187
|
const { thenBranch, elseBranch } = parseIf(node);
|
|
4165
4188
|
if (!thenBranch || !elseBranch) return;
|
|
@@ -4224,10 +4247,9 @@ var hashFunc = (node, localNames) => {
|
|
|
4224
4247
|
};
|
|
4225
4248
|
var dedupe = (ast) => {
|
|
4226
4249
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4227
|
-
const result = clone2(ast);
|
|
4228
4250
|
const signatures = /* @__PURE__ */ new Map();
|
|
4229
4251
|
const redirects = /* @__PURE__ */ new Map();
|
|
4230
|
-
for (const node of
|
|
4252
|
+
for (const node of ast.slice(1)) {
|
|
4231
4253
|
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
4232
4254
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4233
4255
|
if (!name2) continue;
|
|
@@ -4247,8 +4269,8 @@ var dedupe = (ast) => {
|
|
|
4247
4269
|
signatures.set(hash, name2);
|
|
4248
4270
|
}
|
|
4249
4271
|
}
|
|
4250
|
-
if (redirects.size === 0) return
|
|
4251
|
-
walkPost2(
|
|
4272
|
+
if (redirects.size === 0) return ast;
|
|
4273
|
+
walkPost2(ast, (node) => {
|
|
4252
4274
|
if (!Array.isArray(node)) return;
|
|
4253
4275
|
const op = node[0];
|
|
4254
4276
|
if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
|
|
@@ -4270,14 +4292,13 @@ var dedupe = (ast) => {
|
|
|
4270
4292
|
}
|
|
4271
4293
|
}
|
|
4272
4294
|
});
|
|
4273
|
-
return
|
|
4295
|
+
return ast;
|
|
4274
4296
|
};
|
|
4275
4297
|
var dedupTypes = (ast) => {
|
|
4276
4298
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4277
|
-
const result = clone2(ast);
|
|
4278
4299
|
const signatures = /* @__PURE__ */ new Map();
|
|
4279
4300
|
const redirects = /* @__PURE__ */ new Map();
|
|
4280
|
-
for (const node of
|
|
4301
|
+
for (const node of ast.slice(1)) {
|
|
4281
4302
|
if (!Array.isArray(node) || node[0] !== "type") continue;
|
|
4282
4303
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4283
4304
|
if (!name2) continue;
|
|
@@ -4288,15 +4309,15 @@ var dedupTypes = (ast) => {
|
|
|
4288
4309
|
signatures.set(hash, name2);
|
|
4289
4310
|
}
|
|
4290
4311
|
}
|
|
4291
|
-
if (redirects.size === 0) return
|
|
4292
|
-
for (let i =
|
|
4293
|
-
const node =
|
|
4312
|
+
if (redirects.size === 0) return ast;
|
|
4313
|
+
for (let i = ast.length - 1; i >= 0; i--) {
|
|
4314
|
+
const node = ast[i];
|
|
4294
4315
|
if (Array.isArray(node) && node[0] === "type") {
|
|
4295
4316
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4296
|
-
if (name2 && redirects.has(name2))
|
|
4317
|
+
if (name2 && redirects.has(name2)) ast.splice(i, 1);
|
|
4297
4318
|
}
|
|
4298
4319
|
}
|
|
4299
|
-
walkPost2(
|
|
4320
|
+
walkPost2(ast, (node) => {
|
|
4300
4321
|
if (!Array.isArray(node)) return;
|
|
4301
4322
|
const op = node[0];
|
|
4302
4323
|
if (op === "func") {
|
|
@@ -4329,7 +4350,7 @@ var dedupTypes = (ast) => {
|
|
|
4329
4350
|
}
|
|
4330
4351
|
}
|
|
4331
4352
|
});
|
|
4332
|
-
return
|
|
4353
|
+
return ast;
|
|
4333
4354
|
};
|
|
4334
4355
|
var parseDataString = (str2) => {
|
|
4335
4356
|
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
|
|
@@ -4444,8 +4465,7 @@ var mergeDataSegments = (a, b) => {
|
|
|
4444
4465
|
};
|
|
4445
4466
|
var packData = (ast) => {
|
|
4446
4467
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4447
|
-
|
|
4448
|
-
for (const node of result) {
|
|
4468
|
+
for (const node of ast) {
|
|
4449
4469
|
if (!Array.isArray(node) || node[0] !== "data") continue;
|
|
4450
4470
|
let contentStart = 1;
|
|
4451
4471
|
if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
|
|
@@ -4461,8 +4481,8 @@ var packData = (ast) => {
|
|
|
4461
4481
|
}
|
|
4462
4482
|
}
|
|
4463
4483
|
const dataNodes = [];
|
|
4464
|
-
for (let i = 0; i <
|
|
4465
|
-
const node =
|
|
4484
|
+
for (let i = 0; i < ast.length; i++) {
|
|
4485
|
+
const node = ast[i];
|
|
4466
4486
|
if (Array.isArray(node) && node[0] === "data") {
|
|
4467
4487
|
const info = getDataOffset(node);
|
|
4468
4488
|
if (info) {
|
|
@@ -4488,9 +4508,9 @@ var packData = (ast) => {
|
|
|
4488
4508
|
}
|
|
4489
4509
|
}
|
|
4490
4510
|
if (toRemove.size > 0) {
|
|
4491
|
-
|
|
4511
|
+
ast = ast.filter((_, i) => !toRemove.has(i));
|
|
4492
4512
|
}
|
|
4493
|
-
return
|
|
4513
|
+
return ast;
|
|
4494
4514
|
};
|
|
4495
4515
|
var makeShortener = () => {
|
|
4496
4516
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -4509,10 +4529,9 @@ var makeShortener = () => {
|
|
|
4509
4529
|
};
|
|
4510
4530
|
var minifyImports = (ast) => {
|
|
4511
4531
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4512
|
-
const result = clone2(ast);
|
|
4513
4532
|
const shortMod = makeShortener();
|
|
4514
4533
|
const shortField = makeShortener();
|
|
4515
|
-
for (const node of
|
|
4534
|
+
for (const node of ast) {
|
|
4516
4535
|
if (!Array.isArray(node) || node[0] !== "import") continue;
|
|
4517
4536
|
if (typeof node[1] === "string" && node[1][0] === '"') {
|
|
4518
4537
|
node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
|
|
@@ -4521,7 +4540,7 @@ var minifyImports = (ast) => {
|
|
|
4521
4540
|
node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
|
|
4522
4541
|
}
|
|
4523
4542
|
}
|
|
4524
|
-
return
|
|
4543
|
+
return ast;
|
|
4525
4544
|
};
|
|
4526
4545
|
var reorderSafe = (ast) => {
|
|
4527
4546
|
let safe = true;
|
|
@@ -4549,16 +4568,15 @@ var reorderSafe = (ast) => {
|
|
|
4549
4568
|
var reorder = (ast) => {
|
|
4550
4569
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4551
4570
|
if (!reorderSafe(ast)) return ast;
|
|
4552
|
-
const result = clone2(ast);
|
|
4553
4571
|
const callCounts = /* @__PURE__ */ new Map();
|
|
4554
|
-
walk2(
|
|
4572
|
+
walk2(ast, (n) => {
|
|
4555
4573
|
if (!Array.isArray(n)) return;
|
|
4556
4574
|
if (n[0] === "call" || n[0] === "return_call") {
|
|
4557
4575
|
callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
|
|
4558
4576
|
}
|
|
4559
4577
|
});
|
|
4560
4578
|
const imports = [], funcs = [], others = [];
|
|
4561
|
-
for (const node of
|
|
4579
|
+
for (const node of ast.slice(1)) {
|
|
4562
4580
|
if (!Array.isArray(node)) {
|
|
4563
4581
|
others.push(node);
|
|
4564
4582
|
continue;
|
|
@@ -4572,10 +4590,16 @@ var reorder = (ast) => {
|
|
|
4572
4590
|
};
|
|
4573
4591
|
function optimize(ast, opts = true) {
|
|
4574
4592
|
if (typeof ast === "string") ast = parse_default(ast);
|
|
4575
|
-
|
|
4593
|
+
const strictGuard = opts === true;
|
|
4576
4594
|
opts = normalize3(opts);
|
|
4577
|
-
|
|
4578
|
-
|
|
4595
|
+
const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
|
|
4596
|
+
};
|
|
4597
|
+
const verbose = opts.verbose || opts.log;
|
|
4598
|
+
ast = clone2(ast);
|
|
4599
|
+
let beforeRound = null;
|
|
4600
|
+
for (let round = 0; round < 3; round++) {
|
|
4601
|
+
beforeRound = clone2(ast);
|
|
4602
|
+
const sizeBefore = count(ast);
|
|
4579
4603
|
if (opts.stripmut) ast = stripmut(ast);
|
|
4580
4604
|
if (opts.globals) ast = globals(ast);
|
|
4581
4605
|
if (opts.fold) ast = fold(ast);
|
|
@@ -4598,7 +4622,18 @@ function optimize(ast, opts = true) {
|
|
|
4598
4622
|
if (opts.reorder) ast = reorder(ast);
|
|
4599
4623
|
if (opts.treeshake) ast = treeshake(ast);
|
|
4600
4624
|
if (opts.minifyImports) ast = minifyImports(ast);
|
|
4601
|
-
|
|
4625
|
+
const sizeAfter = count(ast);
|
|
4626
|
+
const delta = sizeAfter - sizeBefore;
|
|
4627
|
+
if (verbose || delta !== 0) {
|
|
4628
|
+
log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} nodes`, delta);
|
|
4629
|
+
}
|
|
4630
|
+
const tolerance = strictGuard ? 0 : 5;
|
|
4631
|
+
if (delta > tolerance) {
|
|
4632
|
+
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta}, reverting`, delta);
|
|
4633
|
+
ast = beforeRound;
|
|
4634
|
+
break;
|
|
4635
|
+
}
|
|
4636
|
+
if (equal(beforeRound, ast)) break;
|
|
4602
4637
|
}
|
|
4603
4638
|
return ast;
|
|
4604
4639
|
}
|