watr 4.5.0 → 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;
@@ -3863,19 +3889,23 @@ var inline = (ast) => {
3863
3889
  if (params && !hasLocals && !hasExport && params.length <= 4 && body.length === 1) {
3864
3890
  const paramNames = new Set(params.map((p) => p.name));
3865
3891
  let mutatesParam = false;
3892
+ let hasReturn = false;
3866
3893
  walk2(body[0], (n) => {
3867
3894
  if (!Array.isArray(n)) return;
3868
3895
  if ((n[0] === "local.set" || n[0] === "local.tee") && paramNames.has(n[1])) {
3869
3896
  mutatesParam = true;
3870
3897
  }
3898
+ if (n[0] === "return" || n[0] === "return_call" || n[0] === "return_call_indirect") {
3899
+ hasReturn = true;
3900
+ }
3871
3901
  });
3872
- if (!mutatesParam) {
3902
+ if (!mutatesParam && !hasReturn) {
3873
3903
  inlinable.set(name2, { body: body[0], params });
3874
3904
  }
3875
3905
  }
3876
3906
  }
3877
- if (inlinable.size === 0) return result;
3878
- walkPost2(result, (node) => {
3907
+ if (inlinable.size === 0) return ast;
3908
+ walkPost2(ast, (node) => {
3879
3909
  if (!Array.isArray(node) || node[0] !== "call") return;
3880
3910
  const fname = node[1];
3881
3911
  if (!inlinable.has(fname)) return;
@@ -3895,10 +3925,10 @@ var inline = (ast) => {
3895
3925
  });
3896
3926
  return substituted;
3897
3927
  });
3898
- return result;
3928
+ return ast;
3899
3929
  };
3900
3930
  var vacuum = (ast) => {
3901
- return walkPost2(clone2(ast), (node) => {
3931
+ return walkPost2(ast, (node) => {
3902
3932
  if (!Array.isArray(node)) return;
3903
3933
  const op = node[0];
3904
3934
  if (op === "nop") return ["nop"];
@@ -3997,7 +4027,7 @@ var PEEPHOLE = {
3997
4027
  "local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
3998
4028
  };
3999
4029
  var peephole = (ast) => {
4000
- return walkPost2(clone2(ast), (node) => {
4030
+ return walkPost2(ast, (node) => {
4001
4031
  if (!Array.isArray(node) || node.length !== 3) return;
4002
4032
  const fn = PEEPHOLE[node[0]];
4003
4033
  if (!fn) return;
@@ -4007,10 +4037,9 @@ var peephole = (ast) => {
4007
4037
  };
4008
4038
  var globals = (ast) => {
4009
4039
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4010
- const result = clone2(ast);
4011
4040
  const constGlobals = /* @__PURE__ */ new Map();
4012
4041
  const mutableGlobals = /* @__PURE__ */ new Set();
4013
- for (const node of result.slice(1)) {
4042
+ for (const node of ast.slice(1)) {
4014
4043
  if (!Array.isArray(node) || node[0] !== "global") continue;
4015
4044
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4016
4045
  if (!name2) continue;
@@ -4021,21 +4050,21 @@ var globals = (ast) => {
4021
4050
  const init = node[initIdx];
4022
4051
  if (getConst(init)) constGlobals.set(name2, init);
4023
4052
  }
4024
- walk2(result, (n) => {
4053
+ walk2(ast, (n) => {
4025
4054
  if (!Array.isArray(n) || n[0] !== "global.set") return;
4026
4055
  const ref = n[1];
4027
4056
  if (typeof ref === "string" && ref[0] === "$") mutableGlobals.add(ref);
4028
4057
  });
4029
4058
  for (const name2 of mutableGlobals) constGlobals.delete(name2);
4030
- if (constGlobals.size === 0) return result;
4031
- return walkPost2(result, (node) => {
4059
+ if (constGlobals.size === 0) return ast;
4060
+ return walkPost2(ast, (node) => {
4032
4061
  if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
4033
4062
  const ref = node[1];
4034
4063
  if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
4035
4064
  });
4036
4065
  };
4037
4066
  var offset = (ast) => {
4038
- return walkPost2(clone2(ast), (node) => {
4067
+ return walkPost2(ast, (node) => {
4039
4068
  if (!Array.isArray(node)) return;
4040
4069
  const op = node[0];
4041
4070
  if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
@@ -4086,8 +4115,7 @@ var offset = (ast) => {
4086
4115
  });
4087
4116
  };
4088
4117
  var unbranch = (ast) => {
4089
- const result = clone2(ast);
4090
- walk2(result, (node) => {
4118
+ walk2(ast, (node) => {
4091
4119
  if (!Array.isArray(node)) return;
4092
4120
  const op = node[0];
4093
4121
  if (op !== "block") return;
@@ -4116,16 +4144,15 @@ var unbranch = (ast) => {
4116
4144
  node.splice(lastIdx, 1);
4117
4145
  }
4118
4146
  });
4119
- return result;
4147
+ return ast;
4120
4148
  };
4121
4149
  var stripmut = (ast) => {
4122
4150
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4123
- const result = clone2(ast);
4124
4151
  const written = /* @__PURE__ */ new Set();
4125
- walk2(result, (n) => {
4152
+ walk2(ast, (n) => {
4126
4153
  if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
4127
4154
  });
4128
- return walkPost2(result, (node) => {
4155
+ return walkPost2(ast, (node) => {
4129
4156
  if (!Array.isArray(node) || node[0] !== "global") return;
4130
4157
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4131
4158
  if (!name2 || written.has(name2)) return;
@@ -4139,7 +4166,7 @@ var stripmut = (ast) => {
4139
4166
  });
4140
4167
  };
4141
4168
  var brif = (ast) => {
4142
- return walkPost2(clone2(ast), (node) => {
4169
+ return walkPost2(ast, (node) => {
4143
4170
  if (!Array.isArray(node) || node[0] !== "if") return;
4144
4171
  const { cond, thenBranch, elseBranch } = parseIf(node);
4145
4172
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4155,7 +4182,7 @@ var brif = (ast) => {
4155
4182
  });
4156
4183
  };
4157
4184
  var foldarms = (ast) => {
4158
- return walkPost2(clone2(ast), (node) => {
4185
+ return walkPost2(ast, (node) => {
4159
4186
  if (!Array.isArray(node) || node[0] !== "if") return;
4160
4187
  const { thenBranch, elseBranch } = parseIf(node);
4161
4188
  if (!thenBranch || !elseBranch) return;
@@ -4220,10 +4247,9 @@ var hashFunc = (node, localNames) => {
4220
4247
  };
4221
4248
  var dedupe = (ast) => {
4222
4249
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4223
- const result = clone2(ast);
4224
4250
  const signatures = /* @__PURE__ */ new Map();
4225
4251
  const redirects = /* @__PURE__ */ new Map();
4226
- for (const node of result.slice(1)) {
4252
+ for (const node of ast.slice(1)) {
4227
4253
  if (!Array.isArray(node) || node[0] !== "func") continue;
4228
4254
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4229
4255
  if (!name2) continue;
@@ -4243,8 +4269,8 @@ var dedupe = (ast) => {
4243
4269
  signatures.set(hash, name2);
4244
4270
  }
4245
4271
  }
4246
- if (redirects.size === 0) return result;
4247
- walkPost2(result, (node) => {
4272
+ if (redirects.size === 0) return ast;
4273
+ walkPost2(ast, (node) => {
4248
4274
  if (!Array.isArray(node)) return;
4249
4275
  const op = node[0];
4250
4276
  if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
@@ -4266,14 +4292,13 @@ var dedupe = (ast) => {
4266
4292
  }
4267
4293
  }
4268
4294
  });
4269
- return result;
4295
+ return ast;
4270
4296
  };
4271
4297
  var dedupTypes = (ast) => {
4272
4298
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4273
- const result = clone2(ast);
4274
4299
  const signatures = /* @__PURE__ */ new Map();
4275
4300
  const redirects = /* @__PURE__ */ new Map();
4276
- for (const node of result.slice(1)) {
4301
+ for (const node of ast.slice(1)) {
4277
4302
  if (!Array.isArray(node) || node[0] !== "type") continue;
4278
4303
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4279
4304
  if (!name2) continue;
@@ -4284,15 +4309,15 @@ var dedupTypes = (ast) => {
4284
4309
  signatures.set(hash, name2);
4285
4310
  }
4286
4311
  }
4287
- if (redirects.size === 0) return result;
4288
- for (let i = result.length - 1; i >= 0; i--) {
4289
- 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];
4290
4315
  if (Array.isArray(node) && node[0] === "type") {
4291
4316
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4292
- if (name2 && redirects.has(name2)) result.splice(i, 1);
4317
+ if (name2 && redirects.has(name2)) ast.splice(i, 1);
4293
4318
  }
4294
4319
  }
4295
- walkPost2(result, (node) => {
4320
+ walkPost2(ast, (node) => {
4296
4321
  if (!Array.isArray(node)) return;
4297
4322
  const op = node[0];
4298
4323
  if (op === "func") {
@@ -4325,7 +4350,7 @@ var dedupTypes = (ast) => {
4325
4350
  }
4326
4351
  }
4327
4352
  });
4328
- return result;
4353
+ return ast;
4329
4354
  };
4330
4355
  var parseDataString = (str2) => {
4331
4356
  if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
@@ -4440,8 +4465,7 @@ var mergeDataSegments = (a, b) => {
4440
4465
  };
4441
4466
  var packData = (ast) => {
4442
4467
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4443
- let result = clone2(ast);
4444
- for (const node of result) {
4468
+ for (const node of ast) {
4445
4469
  if (!Array.isArray(node) || node[0] !== "data") continue;
4446
4470
  let contentStart = 1;
4447
4471
  if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
@@ -4457,8 +4481,8 @@ var packData = (ast) => {
4457
4481
  }
4458
4482
  }
4459
4483
  const dataNodes = [];
4460
- for (let i = 0; i < result.length; i++) {
4461
- const node = result[i];
4484
+ for (let i = 0; i < ast.length; i++) {
4485
+ const node = ast[i];
4462
4486
  if (Array.isArray(node) && node[0] === "data") {
4463
4487
  const info = getDataOffset(node);
4464
4488
  if (info) {
@@ -4484,9 +4508,9 @@ var packData = (ast) => {
4484
4508
  }
4485
4509
  }
4486
4510
  if (toRemove.size > 0) {
4487
- result = result.filter((_, i) => !toRemove.has(i));
4511
+ ast = ast.filter((_, i) => !toRemove.has(i));
4488
4512
  }
4489
- return result;
4513
+ return ast;
4490
4514
  };
4491
4515
  var makeShortener = () => {
4492
4516
  const map = /* @__PURE__ */ new Map();
@@ -4505,10 +4529,9 @@ var makeShortener = () => {
4505
4529
  };
4506
4530
  var minifyImports = (ast) => {
4507
4531
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4508
- const result = clone2(ast);
4509
4532
  const shortMod = makeShortener();
4510
4533
  const shortField = makeShortener();
4511
- for (const node of result) {
4534
+ for (const node of ast) {
4512
4535
  if (!Array.isArray(node) || node[0] !== "import") continue;
4513
4536
  if (typeof node[1] === "string" && node[1][0] === '"') {
4514
4537
  node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
@@ -4517,7 +4540,7 @@ var minifyImports = (ast) => {
4517
4540
  node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
4518
4541
  }
4519
4542
  }
4520
- return result;
4543
+ return ast;
4521
4544
  };
4522
4545
  var reorderSafe = (ast) => {
4523
4546
  let safe = true;
@@ -4545,16 +4568,15 @@ var reorderSafe = (ast) => {
4545
4568
  var reorder = (ast) => {
4546
4569
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4547
4570
  if (!reorderSafe(ast)) return ast;
4548
- const result = clone2(ast);
4549
4571
  const callCounts = /* @__PURE__ */ new Map();
4550
- walk2(result, (n) => {
4572
+ walk2(ast, (n) => {
4551
4573
  if (!Array.isArray(n)) return;
4552
4574
  if (n[0] === "call" || n[0] === "return_call") {
4553
4575
  callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
4554
4576
  }
4555
4577
  });
4556
4578
  const imports = [], funcs = [], others = [];
4557
- for (const node of result.slice(1)) {
4579
+ for (const node of ast.slice(1)) {
4558
4580
  if (!Array.isArray(node)) {
4559
4581
  others.push(node);
4560
4582
  continue;
@@ -4568,10 +4590,16 @@ var reorder = (ast) => {
4568
4590
  };
4569
4591
  function optimize(ast, opts = true) {
4570
4592
  if (typeof ast === "string") ast = parse_default(ast);
4571
- ast = clone2(ast);
4593
+ const strictGuard = opts === true;
4572
4594
  opts = normalize3(opts);
4573
- for (let round = 0; round < 6; round++) {
4574
- 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);
4575
4603
  if (opts.stripmut) ast = stripmut(ast);
4576
4604
  if (opts.globals) ast = globals(ast);
4577
4605
  if (opts.fold) ast = fold(ast);
@@ -4594,7 +4622,18 @@ function optimize(ast, opts = true) {
4594
4622
  if (opts.reorder) ast = reorder(ast);
4595
4623
  if (opts.treeshake) ast = treeshake(ast);
4596
4624
  if (opts.minifyImports) ast = minifyImports(ast);
4597
- 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;
4598
4637
  }
4599
4638
  return ast;
4600
4639
  }