watr 4.5.1 → 4.6.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 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)
@@ -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());
@@ -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));
@@ -3041,11 +3065,17 @@ var OPTS = {
3041
3065
  branch: true,
3042
3066
  // simplify constant branches
3043
3067
  propagate: true,
3044
- // constant propagation through locals
3045
- inline: true,
3046
- // inline tiny functions
3068
+ // forward-propagate single-use locals & tiny consts (never inflates)
3069
+ inline: false,
3070
+ // inline tiny functions — can duplicate bodies
3071
+ inlineOnce: true,
3072
+ // inline single-call functions into their lone caller (never duplicates)
3047
3073
  vacuum: true,
3048
3074
  // remove nops, drop-of-pure, empty branches
3075
+ mergeBlocks: true,
3076
+ // unwrap `(block $L …)` whose label is never targeted
3077
+ coalesce: true,
3078
+ // share local slots between same-type non-overlapping locals
3049
3079
  peephole: true,
3050
3080
  // x-x→0, x&0→0, etc.
3051
3081
  globals: true,
@@ -3058,14 +3088,12 @@ var OPTS = {
3058
3088
  // strip mut from never-written globals
3059
3089
  brif: true,
3060
3090
  // 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.
3091
+ foldarms: false,
3092
+ // merge identical trailing if arms — can add block wrapper
3065
3093
  dedupe: true,
3066
3094
  // eliminate duplicate functions
3067
- reorder: true,
3068
- // put hot functions first for smaller LEBs
3095
+ reorder: false,
3096
+ // put hot functions first no AST reduction
3069
3097
  dedupTypes: true,
3070
3098
  // merge identical type definitions
3071
3099
  packData: true,
@@ -3074,6 +3102,13 @@ var OPTS = {
3074
3102
  // shorten import names — enable only when you control the host
3075
3103
  };
3076
3104
  var ALL2 = Object.keys(OPTS);
3105
+ var binarySize = (ast) => {
3106
+ try {
3107
+ return compile(ast).length;
3108
+ } catch {
3109
+ return Infinity;
3110
+ }
3111
+ };
3077
3112
  var equal = (a, b) => {
3078
3113
  if (a === b) return true;
3079
3114
  if (typeof a !== typeof b) return false;
@@ -3088,10 +3123,8 @@ var normalize3 = (opts) => {
3088
3123
  if (opts === false) return {};
3089
3124
  if (typeof opts === "string") {
3090
3125
  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")]));
3126
+ if (set.has("all")) return Object.fromEntries(ALL2.map((f) => [f, true]));
3127
+ return Object.fromEntries(ALL2.map((f) => [f, set.has(f)]));
3095
3128
  }
3096
3129
  return { ...OPTS, ...opts };
3097
3130
  };
@@ -3401,7 +3434,7 @@ var makeConst = (type, value) => {
3401
3434
  return null;
3402
3435
  };
3403
3436
  var fold = (ast) => {
3404
- return walkPost2(clone2(ast), (node) => {
3437
+ return walkPost2(ast, (node) => {
3405
3438
  if (!Array.isArray(node)) return;
3406
3439
  const entry = FOLDABLE[node[0]];
3407
3440
  if (!entry) return;
@@ -3464,7 +3497,7 @@ var IDENTITIES = {
3464
3497
  // f * 1 → x (careful with NaN, skip for floats)
3465
3498
  };
3466
3499
  var identity = (ast) => {
3467
- return walkPost2(clone2(ast), (node) => {
3500
+ return walkPost2(ast, (node) => {
3468
3501
  if (!Array.isArray(node) || node.length !== 3) return;
3469
3502
  const fn = IDENTITIES[node[0]];
3470
3503
  if (!fn) return;
@@ -3474,7 +3507,7 @@ var identity = (ast) => {
3474
3507
  });
3475
3508
  };
3476
3509
  var strength = (ast) => {
3477
- return walkPost2(clone2(ast), (node) => {
3510
+ return walkPost2(ast, (node) => {
3478
3511
  if (!Array.isArray(node) || node.length !== 3) return;
3479
3512
  const [op, a, b] = node;
3480
3513
  if (op === "i32.mul") {
@@ -3530,7 +3563,7 @@ var strength = (ast) => {
3530
3563
  });
3531
3564
  };
3532
3565
  var branch = (ast) => {
3533
- return walkPost2(clone2(ast), (node) => {
3566
+ return walkPost2(ast, (node) => {
3534
3567
  if (!Array.isArray(node)) return;
3535
3568
  const op = node[0];
3536
3569
  if (op === "if") {
@@ -3562,8 +3595,7 @@ var branch = (ast) => {
3562
3595
  };
3563
3596
  var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
3564
3597
  var deadcode = (ast) => {
3565
- const result = clone2(ast);
3566
- walk2(result, (node) => {
3598
+ walk2(ast, (node) => {
3567
3599
  if (!Array.isArray(node)) return;
3568
3600
  const kind = node[0];
3569
3601
  if (kind === "func" || kind === "block" || kind === "loop") {
@@ -3577,7 +3609,7 @@ var deadcode = (ast) => {
3577
3609
  }
3578
3610
  }
3579
3611
  });
3580
- return result;
3612
+ return ast;
3581
3613
  };
3582
3614
  var eliminateDeadInBlock = (block) => {
3583
3615
  let terminated = false;
@@ -3609,8 +3641,7 @@ var eliminateDeadInBlock = (block) => {
3609
3641
  }
3610
3642
  };
3611
3643
  var localReuse = (ast) => {
3612
- const result = clone2(ast);
3613
- walk2(result, (node) => {
3644
+ walk2(ast, (node) => {
3614
3645
  if (!Array.isArray(node) || node[0] !== "func") return;
3615
3646
  const localDecls = [];
3616
3647
  const localTypes = /* @__PURE__ */ new Map();
@@ -3647,7 +3678,7 @@ var localReuse = (ast) => {
3647
3678
  }
3648
3679
  }
3649
3680
  });
3650
- return result;
3681
+ return ast;
3651
3682
  };
3652
3683
  var IMPURE_OPS = /* @__PURE__ */ new Set([
3653
3684
  "call",
@@ -3712,7 +3743,29 @@ var countLocalUses = (node) => {
3712
3743
  });
3713
3744
  return counts;
3714
3745
  };
3715
- var canSubst = (k) => getConst(k.val) || k.pure && k.singleUse;
3746
+ var isTinyConst = (node) => {
3747
+ const c = getConst(node);
3748
+ if (!c) return false;
3749
+ if (c.type === "i32") {
3750
+ const v = c.value | 0;
3751
+ return v >= -64 && v <= 63;
3752
+ }
3753
+ if (c.type === "i64") {
3754
+ const v = typeof c.value === "bigint" ? c.value : BigInt(c.value);
3755
+ return v >= -64n && v <= 63n;
3756
+ }
3757
+ return false;
3758
+ };
3759
+ var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val);
3760
+ var purgeRefs = (known, name2) => {
3761
+ for (const [key, tracked] of known) {
3762
+ let refs = false;
3763
+ walk2(tracked.val, (n) => {
3764
+ if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === name2) refs = true;
3765
+ });
3766
+ if (refs) known.delete(key);
3767
+ }
3768
+ };
3716
3769
  var substGets = (node, known) => walkPost2(node, (n) => {
3717
3770
  if (!Array.isArray(n) || n[0] !== "local.get" || n.length !== 2) return;
3718
3771
  const k = typeof n[1] === "string" && known.get(n[1]);
@@ -3727,9 +3780,10 @@ var forwardPropagate = (funcNode, params, useCounts) => {
3727
3780
  if (!Array.isArray(instr2)) continue;
3728
3781
  const op = instr2[0];
3729
3782
  if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
3730
- if (op === "local.set" && instr2.length === 3 && typeof instr2[1] === "string") {
3783
+ if ((op === "local.set" || op === "local.tee") && instr2.length === 3 && typeof instr2[1] === "string") {
3731
3784
  substGets(instr2[2], known);
3732
3785
  const uses = getUseCount(instr2[1]);
3786
+ purgeRefs(known, instr2[1]);
3733
3787
  known.set(instr2[1], {
3734
3788
  val: instr2[2],
3735
3789
  pure: isPure(instr2[2]),
@@ -3755,6 +3809,12 @@ var forwardPropagate = (funcNode, params, useCounts) => {
3755
3809
  const prev = clone2(instr2);
3756
3810
  substGets(instr2, known);
3757
3811
  if (!equal(prev, instr2)) changed = true;
3812
+ walk2(instr2, (n) => {
3813
+ if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
3814
+ known.delete(n[1]);
3815
+ purgeRefs(known, n[1]);
3816
+ }
3817
+ });
3758
3818
  }
3759
3819
  }
3760
3820
  return changed;
@@ -3812,29 +3872,35 @@ var eliminateDeadStores = (funcNode, params, useCounts) => {
3812
3872
  }
3813
3873
  return changed;
3814
3874
  };
3875
+ var isScopeNode = (n) => Array.isArray(n) && (n[0] === "func" || n[0] === "block" || n[0] === "loop" || n[0] === "then" || n[0] === "else");
3815
3876
  var propagate = (ast) => {
3816
- const result = clone2(ast);
3817
- walk2(result, (funcNode) => {
3877
+ walk2(ast, (funcNode) => {
3818
3878
  if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
3819
3879
  const params = /* @__PURE__ */ new Set();
3820
3880
  for (const sub of funcNode)
3821
3881
  if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
3822
- for (let pass = 0; pass < 4; pass++) {
3823
- let changed = false;
3824
- if (forwardPropagate(funcNode, params, countLocalUses(funcNode))) changed = true;
3825
- if (eliminateSetGetPairs(funcNode, params, countLocalUses(funcNode))) changed = true;
3826
- if (createLocalTees(funcNode, params, countLocalUses(funcNode))) changed = true;
3827
- if (eliminateDeadStores(funcNode, params, countLocalUses(funcNode))) changed = true;
3828
- if (!changed) break;
3882
+ const scopes = [];
3883
+ walkPost2(funcNode, (n) => {
3884
+ if (isScopeNode(n)) scopes.push(n);
3885
+ });
3886
+ for (let round = 0; round < 6; round++) {
3887
+ const useCounts = countLocalUses(funcNode);
3888
+ let progressed = false;
3889
+ for (const scope of scopes) {
3890
+ if (forwardPropagate(scope, params, useCounts)) progressed = true;
3891
+ if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
3892
+ if (createLocalTees(scope, params, useCounts)) progressed = true;
3893
+ if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
3894
+ }
3895
+ if (!progressed) break;
3829
3896
  }
3830
3897
  });
3831
- return result;
3898
+ return ast;
3832
3899
  };
3833
3900
  var inline = (ast) => {
3834
3901
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
3835
- const result = clone2(ast);
3836
3902
  const inlinable = /* @__PURE__ */ new Map();
3837
- for (const node of result.slice(1)) {
3903
+ for (const node of ast.slice(1)) {
3838
3904
  if (!Array.isArray(node) || node[0] !== "func") continue;
3839
3905
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3840
3906
  if (!name2) continue;
@@ -3878,8 +3944,8 @@ var inline = (ast) => {
3878
3944
  }
3879
3945
  }
3880
3946
  }
3881
- if (inlinable.size === 0) return result;
3882
- walkPost2(result, (node) => {
3947
+ if (inlinable.size === 0) return ast;
3948
+ walkPost2(ast, (node) => {
3883
3949
  if (!Array.isArray(node) || node[0] !== "call") return;
3884
3950
  const fname = node[1];
3885
3951
  if (!inlinable.has(fname)) return;
@@ -3899,10 +3965,315 @@ var inline = (ast) => {
3899
3965
  });
3900
3966
  return substituted;
3901
3967
  });
3902
- return result;
3968
+ return ast;
3969
+ };
3970
+ var inlineUid = 0;
3971
+ var inlineOnce = (ast) => {
3972
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
3973
+ const HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
3974
+ const bodyStart = (fn) => {
3975
+ let i = 2;
3976
+ while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && HEAD.has(fn[i][0]))) i++;
3977
+ return i;
3978
+ };
3979
+ const isBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
3980
+ const unsafe = (n) => {
3981
+ if (!Array.isArray(n)) return false;
3982
+ const op = n[0];
3983
+ if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
3984
+ if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
3985
+ if (isBranch(op)) {
3986
+ for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
3987
+ }
3988
+ for (let i = 1; i < n.length; i++) if (unsafe(n[i])) return true;
3989
+ return false;
3990
+ };
3991
+ const callsSelf = (n, name2) => {
3992
+ if (!Array.isArray(n)) return false;
3993
+ if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
3994
+ for (let i = 1; i < n.length; i++) if (callsSelf(n[i], name2)) return true;
3995
+ return false;
3996
+ };
3997
+ const collectPinned = (n, pinned) => {
3998
+ if (!Array.isArray(n)) return;
3999
+ const op = n[0];
4000
+ if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
4001
+ else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
4002
+ else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
4003
+ else if (op === "elem") {
4004
+ for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
4005
+ }
4006
+ for (const c of n) collectPinned(c, pinned);
4007
+ };
4008
+ for (let round = 0; round < 16; round++) {
4009
+ const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
4010
+ const funcByName = /* @__PURE__ */ new Map();
4011
+ for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
4012
+ const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
4013
+ const countRefs = (n) => {
4014
+ if (!Array.isArray(n)) return;
4015
+ const op = n[0];
4016
+ if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
4017
+ else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
4018
+ for (let i = 1; i < n.length; i++) countRefs(n[i]);
4019
+ };
4020
+ countRefs(ast);
4021
+ const pinned = /* @__PURE__ */ new Set();
4022
+ for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
4023
+ let calleeName = null;
4024
+ for (const [name2, fn] of funcByName) {
4025
+ if (pinned.has(name2) || otherRef.has(name2)) continue;
4026
+ if (callRefs.get(name2) !== 1) continue;
4027
+ if (callsSelf(fn, name2)) continue;
4028
+ let ok = true, nResult = 0;
4029
+ for (let i = 2; i < fn.length; i++) {
4030
+ const c = fn[i];
4031
+ if (typeof c === "string") continue;
4032
+ if (!Array.isArray(c)) {
4033
+ ok = false;
4034
+ break;
4035
+ }
4036
+ if (c[0] === "param" || c[0] === "local") {
4037
+ if (typeof c[1] !== "string" || c[1][0] !== "$") {
4038
+ ok = false;
4039
+ break;
4040
+ }
4041
+ } else if (c[0] === "result") nResult += c.length - 1;
4042
+ else if (c[0] === "export") {
4043
+ ok = false;
4044
+ break;
4045
+ } else if (c[0] === "type") continue;
4046
+ else break;
4047
+ }
4048
+ if (!ok || nResult > 1) continue;
4049
+ let bad = false;
4050
+ for (let i = bodyStart(fn); i < fn.length; i++) if (unsafe(fn[i])) {
4051
+ bad = true;
4052
+ break;
4053
+ }
4054
+ if (bad) continue;
4055
+ calleeName = name2;
4056
+ break;
4057
+ }
4058
+ if (!calleeName) break;
4059
+ const callee = funcByName.get(calleeName);
4060
+ const params = [], locals = [];
4061
+ let resultType = null;
4062
+ for (let i = 2; i < callee.length; i++) {
4063
+ const c = callee[i];
4064
+ if (typeof c === "string" || !Array.isArray(c)) continue;
4065
+ if (c[0] === "param") params.push({ name: c[1], type: c[2] });
4066
+ else if (c[0] === "result") {
4067
+ if (c.length > 1) resultType = c[1];
4068
+ } else if (c[0] === "local") locals.push({ name: c[1], type: c[2] });
4069
+ else if (c[0] === "export" || c[0] === "type") continue;
4070
+ else break;
4071
+ }
4072
+ const cBody = callee.slice(bodyStart(callee));
4073
+ const uid2 = ++inlineUid;
4074
+ const exit = `$__inl${uid2}`;
4075
+ const rename = /* @__PURE__ */ new Map();
4076
+ for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
4077
+ for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
4078
+ const isBlockLabel = (op) => op === "block" || op === "loop" || op === "if";
4079
+ const labelRename = /* @__PURE__ */ new Map();
4080
+ const collectLabels = (n) => {
4081
+ if (!Array.isArray(n)) return;
4082
+ if (isBlockLabel(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4083
+ labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
4084
+ for (let i = 1; i < n.length; i++) collectLabels(n[i]);
4085
+ };
4086
+ for (const n of cBody) collectLabels(n);
4087
+ const sub = (n) => {
4088
+ if (!Array.isArray(n)) return n;
4089
+ const op = n[0];
4090
+ if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
4091
+ return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
4092
+ if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
4093
+ if (isBlockLabel(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4094
+ return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
4095
+ if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
4096
+ return n.map((c, i) => i === 0 ? c : sub(c));
4097
+ };
4098
+ let done = false;
4099
+ for (const fn of funcs) {
4100
+ if (fn === callee || done) continue;
4101
+ const start = bodyStart(fn);
4102
+ for (let i = start; i < fn.length; i++) {
4103
+ const replaced = walkPost2(fn[i], (n) => {
4104
+ if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
4105
+ const args = n.slice(2);
4106
+ if (args.length !== params.length) return;
4107
+ const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
4108
+ const inner = cBody.map(sub);
4109
+ done = true;
4110
+ return resultType ? ["block", exit, ["result", resultType], ...setup, ...inner] : ["block", exit, ...setup, ...inner];
4111
+ });
4112
+ if (replaced !== fn[i]) fn[i] = replaced;
4113
+ if (done) {
4114
+ const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
4115
+ if (decls.length) fn.splice(bodyStart(fn), 0, ...decls);
4116
+ break;
4117
+ }
4118
+ }
4119
+ if (done) break;
4120
+ }
4121
+ if (!done) break;
4122
+ const idx = ast.indexOf(callee);
4123
+ if (idx >= 0) ast.splice(idx, 1);
4124
+ }
4125
+ return ast;
4126
+ };
4127
+ var targetsLabel = (body, label) => {
4128
+ let found = false;
4129
+ const search = (n, shadowed) => {
4130
+ if (found || !Array.isArray(n)) return;
4131
+ const op = n[0];
4132
+ let inner = shadowed;
4133
+ if ((op === "block" || op === "loop") && typeof n[1] === "string" && n[1] === label) inner = true;
4134
+ if (!shadowed) {
4135
+ if (op === "br" || op === "br_if" || op === "br_on_null" || op === "br_on_non_null" || op === "br_on_cast" || op === "br_on_cast_fail") {
4136
+ if (n[1] === label) {
4137
+ found = true;
4138
+ return;
4139
+ }
4140
+ } else if (op === "br_table") {
4141
+ for (let j = 1; j < n.length; j++) {
4142
+ if (typeof n[j] === "string") {
4143
+ if (n[j] === label) {
4144
+ found = true;
4145
+ return;
4146
+ }
4147
+ } else break;
4148
+ }
4149
+ }
4150
+ }
4151
+ for (let i = 1; i < n.length; i++) search(n[i], inner);
4152
+ };
4153
+ for (const node of body) search(node, false);
4154
+ return found;
4155
+ };
4156
+ var mergeBlocks = (ast) => {
4157
+ walk2(ast, (node) => {
4158
+ if (!isScopeNode(node)) return;
4159
+ let i = 1;
4160
+ while (i < node.length) {
4161
+ const child = node[i];
4162
+ if (!Array.isArray(child) || child[0] !== "block") {
4163
+ i++;
4164
+ continue;
4165
+ }
4166
+ let bi = 1, label = null;
4167
+ if (typeof child[1] === "string" && child[1][0] === "$") {
4168
+ label = child[1];
4169
+ bi = 2;
4170
+ }
4171
+ let typed = false;
4172
+ for (let j = bi; j < child.length; j++) {
4173
+ const c = child[j];
4174
+ if (Array.isArray(c) && (c[0] === "param" || c[0] === "result" || c[0] === "type")) {
4175
+ typed = true;
4176
+ break;
4177
+ }
4178
+ }
4179
+ if (typed) {
4180
+ i++;
4181
+ continue;
4182
+ }
4183
+ const body = child.slice(bi);
4184
+ if (label && targetsLabel(body, label)) {
4185
+ i++;
4186
+ continue;
4187
+ }
4188
+ node.splice(i, 1, ...body);
4189
+ i += body.length;
4190
+ }
4191
+ });
4192
+ return ast;
4193
+ };
4194
+ var coalesceLocals = (ast) => {
4195
+ walk2(ast, (funcNode) => {
4196
+ if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
4197
+ const decls = /* @__PURE__ */ new Map();
4198
+ for (const sub of funcNode) {
4199
+ if (Array.isArray(sub) && sub[0] === "local" && typeof sub[1] === "string" && sub[1][0] === "$" && typeof sub[2] === "string") {
4200
+ decls.set(sub[1], sub[2]);
4201
+ }
4202
+ }
4203
+ if (decls.size < 2) return;
4204
+ const uses = /* @__PURE__ */ new Map();
4205
+ const loopStack = [];
4206
+ let pos = 0, abort = false, condDepth = 0;
4207
+ const visit = (n) => {
4208
+ if (abort || !Array.isArray(n)) return;
4209
+ const op = n[0];
4210
+ const isLoop = op === "loop";
4211
+ if (isLoop) loopStack.push({ start: pos, end: pos });
4212
+ const isSet = op === "local.set" || op === "local.tee";
4213
+ if (isSet || op === "local.get") {
4214
+ const name2 = n[1];
4215
+ if (typeof name2 !== "string" || name2[0] !== "$") {
4216
+ abort = true;
4217
+ return;
4218
+ }
4219
+ if (isSet) for (let i = 2; i < n.length; i++) visit(n[i]);
4220
+ const here = pos++;
4221
+ if (decls.has(name2)) {
4222
+ let u = uses.get(name2);
4223
+ if (!u) {
4224
+ u = { start: here, end: here, firstOp: op, firstCond: condDepth > 0, loops: /* @__PURE__ */ new Set() };
4225
+ uses.set(name2, u);
4226
+ }
4227
+ if (here > u.end) u.end = here;
4228
+ for (const ls of loopStack) u.loops.add(ls);
4229
+ }
4230
+ } else {
4231
+ pos++;
4232
+ const isIf = op === "if";
4233
+ for (let i = 1; i < n.length; i++) {
4234
+ const c = n[i];
4235
+ const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
4236
+ if (cond) condDepth++;
4237
+ visit(c);
4238
+ if (cond) condDepth--;
4239
+ }
4240
+ }
4241
+ if (isLoop) {
4242
+ const ls = loopStack.pop();
4243
+ ls.end = pos;
4244
+ }
4245
+ };
4246
+ visit(funcNode);
4247
+ if (abort) return;
4248
+ for (const u of uses.values()) {
4249
+ for (const ls of u.loops) {
4250
+ if (ls.start < u.start) u.start = ls.start;
4251
+ if (ls.end > u.end) u.end = ls.end;
4252
+ }
4253
+ }
4254
+ const ordered = [...uses.entries()].sort((a, b) => a[1].start - b[1].start);
4255
+ const rename = /* @__PURE__ */ new Map();
4256
+ const slots = [];
4257
+ for (const [name2, range] of ordered) {
4258
+ const readsZero = range.firstOp === "local.get" || range.firstCond;
4259
+ const type = decls.get(name2);
4260
+ const slot = readsZero ? null : slots.find((s) => s.type === type && s.end < range.start);
4261
+ if (slot) {
4262
+ rename.set(name2, slot.primary);
4263
+ if (range.end > slot.end) slot.end = range.end;
4264
+ } else slots.push({ primary: name2, type, end: range.end });
4265
+ }
4266
+ if (rename.size === 0) return;
4267
+ walk2(funcNode, (n) => {
4268
+ if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
4269
+ n[1] = rename.get(n[1]);
4270
+ }
4271
+ });
4272
+ });
4273
+ return ast;
3903
4274
  };
3904
4275
  var vacuum = (ast) => {
3905
- return walkPost2(clone2(ast), (node) => {
4276
+ return walkPost2(ast, (node) => {
3906
4277
  if (!Array.isArray(node)) return;
3907
4278
  const op = node[0];
3908
4279
  if (op === "nop") return ["nop"];
@@ -4001,7 +4372,7 @@ var PEEPHOLE = {
4001
4372
  "local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
4002
4373
  };
4003
4374
  var peephole = (ast) => {
4004
- return walkPost2(clone2(ast), (node) => {
4375
+ return walkPost2(ast, (node) => {
4005
4376
  if (!Array.isArray(node) || node.length !== 3) return;
4006
4377
  const fn = PEEPHOLE[node[0]];
4007
4378
  if (!fn) return;
@@ -4009,37 +4380,85 @@ var peephole = (ast) => {
4009
4380
  if (result !== null) return result;
4010
4381
  });
4011
4382
  };
4383
+ var slebSize = (v) => {
4384
+ let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
4385
+ let n = 1;
4386
+ while (true) {
4387
+ const b = x & 0x7fn;
4388
+ x >>= 7n;
4389
+ if (x === 0n && (b & 0x40n) === 0n || x === -1n && (b & 0x40n) !== 0n) return n;
4390
+ n++;
4391
+ }
4392
+ };
4393
+ var constInstrSize = (node) => {
4394
+ if (!Array.isArray(node)) return 4;
4395
+ switch (node[0]) {
4396
+ case "i32.const":
4397
+ case "i64.const":
4398
+ return 1 + slebSize(node[1]);
4399
+ case "f32.const":
4400
+ return 5;
4401
+ case "f64.const":
4402
+ return 9;
4403
+ case "v128.const":
4404
+ return 18;
4405
+ default:
4406
+ return 4;
4407
+ }
4408
+ };
4409
+ var GLOBAL_GET_SIZE = 2;
4012
4410
  var globals = (ast) => {
4013
4411
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4014
- const result = clone2(ast);
4015
4412
  const constGlobals = /* @__PURE__ */ new Map();
4016
- const mutableGlobals = /* @__PURE__ */ new Set();
4017
- for (const node of result.slice(1)) {
4018
- if (!Array.isArray(node) || node[0] !== "global") continue;
4413
+ const exported = /* @__PURE__ */ new Set();
4414
+ for (const node of ast.slice(1)) {
4415
+ if (!Array.isArray(node)) continue;
4416
+ if (node[0] === "export" && Array.isArray(node[2]) && node[2][0] === "global" && typeof node[2][1] === "string") {
4417
+ exported.add(node[2][1]);
4418
+ continue;
4419
+ }
4420
+ if (node[0] !== "global") continue;
4019
4421
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4020
4422
  if (!name2) continue;
4021
- const hasName = typeof node[1] === "string" && node[1][0] === "$";
4022
- const initIdx = hasName ? 3 : 2;
4023
- const typeSlot = hasName ? node[2] : node[1];
4423
+ if (node.some((c) => Array.isArray(c) && c[0] === "export")) exported.add(name2);
4424
+ const typeSlot = node[2];
4024
4425
  if (Array.isArray(typeSlot) && typeSlot[0] === "mut") continue;
4025
- const init = node[initIdx];
4426
+ if (Array.isArray(typeSlot) && typeSlot[0] === "import") continue;
4427
+ const init = node[3];
4026
4428
  if (getConst(init)) constGlobals.set(name2, init);
4027
4429
  }
4028
- walk2(result, (n) => {
4029
- if (!Array.isArray(n) || n[0] !== "global.set") return;
4430
+ if (constGlobals.size === 0) return ast;
4431
+ const reads = /* @__PURE__ */ new Map();
4432
+ walk2(ast, (n) => {
4433
+ if (!Array.isArray(n)) return;
4030
4434
  const ref = n[1];
4031
- if (typeof ref === "string" && ref[0] === "$") mutableGlobals.add(ref);
4435
+ if (typeof ref !== "string" || ref[0] !== "$") return;
4436
+ if (n[0] === "global.set") constGlobals.delete(ref);
4437
+ else if (n[0] === "global.get") reads.set(ref, (reads.get(ref) || 0) + 1);
4032
4438
  });
4033
- for (const name2 of mutableGlobals) constGlobals.delete(name2);
4034
- if (constGlobals.size === 0) return result;
4035
- return walkPost2(result, (node) => {
4439
+ const propagate2 = /* @__PURE__ */ new Set();
4440
+ for (const [name2, init] of constGlobals) {
4441
+ const r = reads.get(name2) || 0;
4442
+ if (r === 0) continue;
4443
+ const cs = constInstrSize(init);
4444
+ const declSize = cs + 2;
4445
+ const before = r * GLOBAL_GET_SIZE + declSize;
4446
+ const after = r * cs + (exported.has(name2) ? declSize : 0);
4447
+ if (after <= before) propagate2.add(name2);
4448
+ }
4449
+ if (propagate2.size === 0) return ast;
4450
+ walkPost2(ast, (node) => {
4036
4451
  if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
4037
- const ref = node[1];
4038
- if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
4452
+ if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
4039
4453
  });
4454
+ for (let i = ast.length - 1; i >= 1; i--) {
4455
+ const n = ast[i];
4456
+ if (Array.isArray(n) && n[0] === "global" && typeof n[1] === "string" && propagate2.has(n[1]) && !exported.has(n[1])) ast.splice(i, 1);
4457
+ }
4458
+ return ast;
4040
4459
  };
4041
4460
  var offset = (ast) => {
4042
- return walkPost2(clone2(ast), (node) => {
4461
+ return walkPost2(ast, (node) => {
4043
4462
  if (!Array.isArray(node)) return;
4044
4463
  const op = node[0];
4045
4464
  if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
@@ -4090,8 +4509,7 @@ var offset = (ast) => {
4090
4509
  });
4091
4510
  };
4092
4511
  var unbranch = (ast) => {
4093
- const result = clone2(ast);
4094
- walk2(result, (node) => {
4512
+ walk2(ast, (node) => {
4095
4513
  if (!Array.isArray(node)) return;
4096
4514
  const op = node[0];
4097
4515
  if (op !== "block") return;
@@ -4117,19 +4535,18 @@ var unbranch = (ast) => {
4117
4535
  if (lastIdx < 0) return;
4118
4536
  const last = node[lastIdx];
4119
4537
  if (Array.isArray(last) && last[0] === "br" && last[1] === label) {
4120
- node.splice(lastIdx, 1);
4538
+ node.splice(lastIdx, 1, ...last.slice(2));
4121
4539
  }
4122
4540
  });
4123
- return result;
4541
+ return ast;
4124
4542
  };
4125
4543
  var stripmut = (ast) => {
4126
4544
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4127
- const result = clone2(ast);
4128
4545
  const written = /* @__PURE__ */ new Set();
4129
- walk2(result, (n) => {
4546
+ walk2(ast, (n) => {
4130
4547
  if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
4131
4548
  });
4132
- return walkPost2(result, (node) => {
4549
+ return walkPost2(ast, (node) => {
4133
4550
  if (!Array.isArray(node) || node[0] !== "global") return;
4134
4551
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4135
4552
  if (!name2 || written.has(name2)) return;
@@ -4143,7 +4560,7 @@ var stripmut = (ast) => {
4143
4560
  });
4144
4561
  };
4145
4562
  var brif = (ast) => {
4146
- return walkPost2(clone2(ast), (node) => {
4563
+ return walkPost2(ast, (node) => {
4147
4564
  if (!Array.isArray(node) || node[0] !== "if") return;
4148
4565
  const { cond, thenBranch, elseBranch } = parseIf(node);
4149
4566
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4159,7 +4576,7 @@ var brif = (ast) => {
4159
4576
  });
4160
4577
  };
4161
4578
  var foldarms = (ast) => {
4162
- return walkPost2(clone2(ast), (node) => {
4579
+ return walkPost2(ast, (node) => {
4163
4580
  if (!Array.isArray(node) || node[0] !== "if") return;
4164
4581
  const { thenBranch, elseBranch } = parseIf(node);
4165
4582
  if (!thenBranch || !elseBranch) return;
@@ -4224,10 +4641,9 @@ var hashFunc = (node, localNames) => {
4224
4641
  };
4225
4642
  var dedupe = (ast) => {
4226
4643
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4227
- const result = clone2(ast);
4228
4644
  const signatures = /* @__PURE__ */ new Map();
4229
4645
  const redirects = /* @__PURE__ */ new Map();
4230
- for (const node of result.slice(1)) {
4646
+ for (const node of ast.slice(1)) {
4231
4647
  if (!Array.isArray(node) || node[0] !== "func") continue;
4232
4648
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4233
4649
  if (!name2) continue;
@@ -4247,8 +4663,8 @@ var dedupe = (ast) => {
4247
4663
  signatures.set(hash, name2);
4248
4664
  }
4249
4665
  }
4250
- if (redirects.size === 0) return result;
4251
- walkPost2(result, (node) => {
4666
+ if (redirects.size === 0) return ast;
4667
+ walkPost2(ast, (node) => {
4252
4668
  if (!Array.isArray(node)) return;
4253
4669
  const op = node[0];
4254
4670
  if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
@@ -4270,14 +4686,13 @@ var dedupe = (ast) => {
4270
4686
  }
4271
4687
  }
4272
4688
  });
4273
- return result;
4689
+ return ast;
4274
4690
  };
4275
4691
  var dedupTypes = (ast) => {
4276
4692
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4277
- const result = clone2(ast);
4278
4693
  const signatures = /* @__PURE__ */ new Map();
4279
4694
  const redirects = /* @__PURE__ */ new Map();
4280
- for (const node of result.slice(1)) {
4695
+ for (const node of ast.slice(1)) {
4281
4696
  if (!Array.isArray(node) || node[0] !== "type") continue;
4282
4697
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4283
4698
  if (!name2) continue;
@@ -4288,15 +4703,15 @@ var dedupTypes = (ast) => {
4288
4703
  signatures.set(hash, name2);
4289
4704
  }
4290
4705
  }
4291
- if (redirects.size === 0) return result;
4292
- for (let i = result.length - 1; i >= 0; i--) {
4293
- const node = result[i];
4706
+ if (redirects.size === 0) return ast;
4707
+ for (let i = ast.length - 1; i >= 0; i--) {
4708
+ const node = ast[i];
4294
4709
  if (Array.isArray(node) && node[0] === "type") {
4295
4710
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4296
- if (name2 && redirects.has(name2)) result.splice(i, 1);
4711
+ if (name2 && redirects.has(name2)) ast.splice(i, 1);
4297
4712
  }
4298
4713
  }
4299
- walkPost2(result, (node) => {
4714
+ walkPost2(ast, (node) => {
4300
4715
  if (!Array.isArray(node)) return;
4301
4716
  const op = node[0];
4302
4717
  if (op === "func") {
@@ -4329,7 +4744,7 @@ var dedupTypes = (ast) => {
4329
4744
  }
4330
4745
  }
4331
4746
  });
4332
- return result;
4747
+ return ast;
4333
4748
  };
4334
4749
  var parseDataString = (str2) => {
4335
4750
  if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
@@ -4444,8 +4859,7 @@ var mergeDataSegments = (a, b) => {
4444
4859
  };
4445
4860
  var packData = (ast) => {
4446
4861
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4447
- let result = clone2(ast);
4448
- for (const node of result) {
4862
+ for (const node of ast) {
4449
4863
  if (!Array.isArray(node) || node[0] !== "data") continue;
4450
4864
  let contentStart = 1;
4451
4865
  if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
@@ -4461,8 +4875,8 @@ var packData = (ast) => {
4461
4875
  }
4462
4876
  }
4463
4877
  const dataNodes = [];
4464
- for (let i = 0; i < result.length; i++) {
4465
- const node = result[i];
4878
+ for (let i = 0; i < ast.length; i++) {
4879
+ const node = ast[i];
4466
4880
  if (Array.isArray(node) && node[0] === "data") {
4467
4881
  const info = getDataOffset(node);
4468
4882
  if (info) {
@@ -4488,9 +4902,9 @@ var packData = (ast) => {
4488
4902
  }
4489
4903
  }
4490
4904
  if (toRemove.size > 0) {
4491
- result = result.filter((_, i) => !toRemove.has(i));
4905
+ ast = ast.filter((_, i) => !toRemove.has(i));
4492
4906
  }
4493
- return result;
4907
+ return ast;
4494
4908
  };
4495
4909
  var makeShortener = () => {
4496
4910
  const map = /* @__PURE__ */ new Map();
@@ -4509,10 +4923,9 @@ var makeShortener = () => {
4509
4923
  };
4510
4924
  var minifyImports = (ast) => {
4511
4925
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4512
- const result = clone2(ast);
4513
4926
  const shortMod = makeShortener();
4514
4927
  const shortField = makeShortener();
4515
- for (const node of result) {
4928
+ for (const node of ast) {
4516
4929
  if (!Array.isArray(node) || node[0] !== "import") continue;
4517
4930
  if (typeof node[1] === "string" && node[1][0] === '"') {
4518
4931
  node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
@@ -4521,7 +4934,7 @@ var minifyImports = (ast) => {
4521
4934
  node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
4522
4935
  }
4523
4936
  }
4524
- return result;
4937
+ return ast;
4525
4938
  };
4526
4939
  var reorderSafe = (ast) => {
4527
4940
  let safe = true;
@@ -4549,16 +4962,15 @@ var reorderSafe = (ast) => {
4549
4962
  var reorder = (ast) => {
4550
4963
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4551
4964
  if (!reorderSafe(ast)) return ast;
4552
- const result = clone2(ast);
4553
4965
  const callCounts = /* @__PURE__ */ new Map();
4554
- walk2(result, (n) => {
4966
+ walk2(ast, (n) => {
4555
4967
  if (!Array.isArray(n)) return;
4556
4968
  if (n[0] === "call" || n[0] === "return_call") {
4557
4969
  callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
4558
4970
  }
4559
4971
  });
4560
4972
  const imports = [], funcs = [], others = [];
4561
- for (const node of result.slice(1)) {
4973
+ for (const node of ast.slice(1)) {
4562
4974
  if (!Array.isArray(node)) {
4563
4975
  others.push(node);
4564
4976
  continue;
@@ -4572,10 +4984,16 @@ var reorder = (ast) => {
4572
4984
  };
4573
4985
  function optimize(ast, opts = true) {
4574
4986
  if (typeof ast === "string") ast = parse_default(ast);
4575
- ast = clone2(ast);
4987
+ const strictGuard = opts === true;
4576
4988
  opts = normalize3(opts);
4577
- for (let round = 0; round < 6; round++) {
4578
- const before = ast;
4989
+ const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
4990
+ };
4991
+ const verbose = opts.verbose || opts.log;
4992
+ ast = clone2(ast);
4993
+ let beforeRound = null;
4994
+ for (let round = 0; round < 3; round++) {
4995
+ beforeRound = clone2(ast);
4996
+ const sizeBefore = binarySize(ast);
4579
4997
  if (opts.stripmut) ast = stripmut(ast);
4580
4998
  if (opts.globals) ast = globals(ast);
4581
4999
  if (opts.fold) ast = fold(ast);
@@ -4584,6 +5002,7 @@ function optimize(ast, opts = true) {
4584
5002
  if (opts.strength) ast = strength(ast);
4585
5003
  if (opts.branch) ast = branch(ast);
4586
5004
  if (opts.propagate) ast = propagate(ast);
5005
+ if (opts.inlineOnce) ast = inlineOnce(ast);
4587
5006
  if (opts.inline) ast = inline(ast);
4588
5007
  if (opts.offset) ast = offset(ast);
4589
5008
  if (opts.unbranch) ast = unbranch(ast);
@@ -4591,6 +5010,8 @@ function optimize(ast, opts = true) {
4591
5010
  if (opts.foldarms) ast = foldarms(ast);
4592
5011
  if (opts.deadcode) ast = deadcode(ast);
4593
5012
  if (opts.vacuum) ast = vacuum(ast);
5013
+ if (opts.mergeBlocks) ast = mergeBlocks(ast);
5014
+ if (opts.coalesce) ast = coalesceLocals(ast);
4594
5015
  if (opts.locals) ast = localReuse(ast);
4595
5016
  if (opts.dedupe) ast = dedupe(ast);
4596
5017
  if (opts.dedupTypes) ast = dedupTypes(ast);
@@ -4598,7 +5019,19 @@ function optimize(ast, opts = true) {
4598
5019
  if (opts.reorder) ast = reorder(ast);
4599
5020
  if (opts.treeshake) ast = treeshake(ast);
4600
5021
  if (opts.minifyImports) ast = minifyImports(ast);
4601
- if (equal(before, ast)) break;
5022
+ if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
5023
+ const sizeAfter = binarySize(ast);
5024
+ const delta = sizeAfter - sizeBefore;
5025
+ if (verbose || delta !== 0) {
5026
+ log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
5027
+ }
5028
+ const tolerance = strictGuard ? 0 : 16;
5029
+ if (delta > tolerance) {
5030
+ if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
5031
+ ast = beforeRound;
5032
+ break;
5033
+ }
5034
+ if (equal(beforeRound, ast)) break;
4602
5035
  }
4603
5036
  return ast;
4604
5037
  }