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 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
- n = n[0] === "-" ? -BigInt(n.slice(1)) : BigInt(n);
144
- if (n < -0x8000000000000000n || n > 0xffffffffffffffffn) err(`i64 constant out of range`);
145
- _i64[0] = n;
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" && ~(idx = input.indexOf("nan:"))) {
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" && ~(idx = input.indexOf("nan:"))) {
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 = parseInt(int);
186
- isNaN(intVal) && err();
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, count = true) => {
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(count ? vec(items) : items.flat())];
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
- const op = node[0];
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 = [], count = 0;
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
- count++;
1878
+ count2++;
1855
1879
  }
1856
1880
  c.block.push(1);
1857
- return [...result, ...uleb(count), ...catches];
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 = [], count = 0;
1866
- while (n[0] && (!isNaN(n[0]) || isId(n[0]))) labels.push(...uleb(blockid(n.shift(), c.block))), count++;
1867
- return [...uleb(count - 1), ...labels];
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: true,
3044
- // constant propagation through locals
3045
- inline: true,
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: true,
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: true,
3068
- // put hot functions first for smaller LEBs
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.size === 1 && ALL2.includes([...set][0])) {
3092
- return Object.fromEntries(ALL2.map((f) => [f, set.has(f)]));
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(clone2(ast), (node) => {
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(clone2(ast), (node) => {
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(clone2(ast), (node) => {
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(clone2(ast), (node) => {
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
- const result = clone2(ast);
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 result;
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
- const result = clone2(ast);
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 result;
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
- const result = clone2(ast);
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 result;
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 result.slice(1)) {
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 result;
3882
- walkPost2(result, (node) => {
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 result;
3928
+ return ast;
3903
3929
  };
3904
3930
  var vacuum = (ast) => {
3905
- return walkPost2(clone2(ast), (node) => {
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(clone2(ast), (node) => {
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 result.slice(1)) {
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(result, (n) => {
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 result;
4035
- return walkPost2(result, (node) => {
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(clone2(ast), (node) => {
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
- const result = clone2(ast);
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 result;
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(result, (n) => {
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(result, (node) => {
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(clone2(ast), (node) => {
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(clone2(ast), (node) => {
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 result.slice(1)) {
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 result;
4251
- walkPost2(result, (node) => {
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 result;
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 result.slice(1)) {
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 result;
4292
- for (let i = result.length - 1; i >= 0; i--) {
4293
- const node = result[i];
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)) result.splice(i, 1);
4317
+ if (name2 && redirects.has(name2)) ast.splice(i, 1);
4297
4318
  }
4298
4319
  }
4299
- walkPost2(result, (node) => {
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 result;
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
- let result = clone2(ast);
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 < result.length; i++) {
4465
- const node = result[i];
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
- result = result.filter((_, i) => !toRemove.has(i));
4511
+ ast = ast.filter((_, i) => !toRemove.has(i));
4492
4512
  }
4493
- return result;
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 result) {
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 result;
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(result, (n) => {
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 result.slice(1)) {
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
- ast = clone2(ast);
4593
+ const strictGuard = opts === true;
4576
4594
  opts = normalize3(opts);
4577
- for (let round = 0; round < 6; round++) {
4578
- const before = ast;
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
- if (equal(before, ast)) break;
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
  }