@markw65/monkeyc-optimizer 1.1.12 → 1.1.14

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/build/api.cjs CHANGED
@@ -2161,8 +2161,9 @@ function inlineRequested(state, func) {
2161
2161
  return false;
2162
2162
  }
2163
2163
  function shouldInline(state, func, call, context) {
2164
- if (state.inlining)
2164
+ if (state.inlining || (state.localsStack?.length ?? 0) <= 1) {
2165
2165
  return false;
2166
+ }
2166
2167
  let autoInline = false;
2167
2168
  let inlineAsExpression = false;
2168
2169
  const args = call.arguments;
@@ -2758,13 +2759,15 @@ function fixNodeScope(state, lookupNode, nodeStack) {
2758
2759
  /* harmony import */ var _type_flow__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(4859);
2759
2760
  /* harmony import */ var _type_flow_could_be__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(4055);
2760
2761
  /* harmony import */ var _type_flow_interp__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(7161);
2761
- /* harmony import */ var _type_flow_optimize__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(3687);
2762
- /* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(9234);
2763
- /* harmony import */ var _type_flow_types__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(7255);
2764
- /* harmony import */ var _unused_exprs__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(424);
2765
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(6906);
2766
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_16__);
2767
- /* harmony import */ var _variable_renamer__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(4405);
2762
+ /* harmony import */ var _type_flow_minimize_modules__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(4416);
2763
+ /* harmony import */ var _type_flow_optimize__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(3687);
2764
+ /* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(9234);
2765
+ /* harmony import */ var _type_flow_types__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(7255);
2766
+ /* harmony import */ var _unused_exprs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(424);
2767
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(6906);
2768
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_17__);
2769
+ /* harmony import */ var _variable_renamer__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(4405);
2770
+
2768
2771
 
2769
2772
 
2770
2773
 
@@ -3370,32 +3373,30 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3370
3373
  break;
3371
3374
  case "Identifier": {
3372
3375
  const map = topLocals().map;
3373
- if (map) {
3374
- if (hasProperty(map, node.name)) {
3375
- const name = map[node.name];
3376
- if (typeof name === "string") {
3377
- renameIdentifier(node, name);
3378
- }
3379
- const [, results] = state.lookupValue(node);
3380
- if (results) {
3381
- if (results.length !== 1 || results[0].results.length !== 1) {
3382
- throw new Error(`Local ${node.name} had multiple lookup results`);
3383
- }
3384
- const parent = results[0].parent;
3385
- if (!parent) {
3386
- throw new Error(`Local ${node.name} had no parent`);
3387
- }
3388
- const decl = results[0].results[0];
3389
- if (parent.type === "FunctionDeclaration" ||
3390
- decl.type !== "VariableDeclarator") {
3391
- // we can't optimize away function or catch parameters
3392
- return [];
3393
- }
3394
- if (parent.type !== "BlockStatement") {
3395
- throw new Error(`Local ${node.name} was not declared at block scope(??)`);
3396
- }
3397
- decl.used = true;
3376
+ if (hasProperty(map, node.name)) {
3377
+ const name = map[node.name];
3378
+ if (typeof name === "string") {
3379
+ renameIdentifier(node, name);
3380
+ }
3381
+ const [, results] = state.lookupValue(node);
3382
+ if (results) {
3383
+ if (results.length !== 1 || results[0].results.length !== 1) {
3384
+ throw new Error(`Local ${node.name} had multiple lookup results`);
3385
+ }
3386
+ const parent = results[0].parent;
3387
+ if (!parent) {
3388
+ throw new Error(`Local ${node.name} had no parent`);
3389
+ }
3390
+ const decl = results[0].results[0];
3391
+ if (parent.type === "FunctionDeclaration" ||
3392
+ decl.type !== "VariableDeclarator") {
3393
+ // we can't optimize away function or catch parameters
3394
+ return [];
3395
+ }
3396
+ if (parent.type !== "BlockStatement") {
3397
+ throw new Error(`Local ${node.name} was not declared at block scope(??)`);
3398
3398
  }
3399
+ decl.used = true;
3399
3400
  }
3400
3401
  }
3401
3402
  return [];
@@ -3683,6 +3684,11 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3683
3684
  state.nextExposed = {};
3684
3685
  delete state.pre;
3685
3686
  delete state.post;
3687
+ if (state.config?.minimizeModules ?? true) {
3688
+ Object.values(fnMap).forEach((f) => {
3689
+ minimizeModules(f.ast, state);
3690
+ });
3691
+ }
3686
3692
  Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
3687
3693
  const cleanup = (node) => {
3688
3694
  switch (node.type) {
@@ -4026,7 +4032,7 @@ function pragmaChecker(state, ast, diagnostics) {
4026
4032
  if (kind === "match") {
4027
4033
  const haystack = formatAst(node)
4028
4034
  .replace(/([\r\n]|\s)+/g, " ")
4029
- .replace(/\b\w+\s\/\*>(\w+)<\*\//g, "$1");
4035
+ .replace(/\b\w+\s\/\*>([\w.]+)<\*\//g, "$1");
4030
4036
  if (!matcher(quote, needle, haystack)) {
4031
4037
  matcher(quote, needle, haystack);
4032
4038
  diagnostic(state, comment, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
@@ -4951,8 +4957,8 @@ function buildConflictGraph(state, func) {
4951
4957
  function addEquiv(ts, key, equiv) {
4952
4958
  if (key === equiv)
4953
4959
  return true;
4954
- let keyVal = ts.get(key);
4955
- let equivVal = ts.get(equiv);
4960
+ const keyVal = ts.get(key);
4961
+ const equivVal = ts.get(equiv);
4956
4962
  if (!keyVal || !equivVal)
4957
4963
  return false;
4958
4964
  if (equivVal.equivSet) {
@@ -4964,31 +4970,25 @@ function addEquiv(ts, key, equiv) {
4964
4970
  }
4965
4971
  // equiv is not (or no longer) part of an equivSet
4966
4972
  if (!keyVal.equivSet) {
4967
- keyVal = { ...keyVal };
4968
4973
  keyVal.equivSet = new Set([key, equiv]);
4969
- ts.set(key, keyVal);
4970
4974
  }
4971
4975
  else {
4972
4976
  keyVal.equivSet.add(equiv);
4973
4977
  }
4974
- equivVal = { ...equivVal, equivSet: keyVal.equivSet };
4975
- ts.set(equiv, equivVal);
4978
+ equivVal.equivSet = keyVal.equivSet;
4976
4979
  return false;
4977
4980
  }
4978
4981
  function removeEquiv(ts, equiv) {
4979
- let equivVal = ts.get(equiv);
4982
+ const equivVal = ts.get(equiv);
4980
4983
  if (!equivVal?.equivSet)
4981
4984
  return;
4982
4985
  equivVal.equivSet.delete(equiv);
4983
4986
  if (equivVal.equivSet.size === 1) {
4984
4987
  const other = Array.from(equivVal.equivSet)[0];
4985
- const otherVal = { ...ts.get(other) };
4988
+ const otherVal = ts.get(other);
4986
4989
  delete otherVal.equivSet;
4987
- ts.set(other, otherVal);
4988
4990
  }
4989
- equivVal = { ...equivVal };
4990
4991
  delete equivVal.equivSet;
4991
- ts.set(equiv, equivVal);
4992
4992
  }
4993
4993
  function getEquivSet(ts, k) {
4994
4994
  const keys = ts.get(k)?.equivSet;
@@ -5042,9 +5042,14 @@ function cloneTypeState(blockState) {
5042
5042
  const { map, trackedMemberDecls, liveCopyPropEvents, ...rest } = blockState;
5043
5043
  const clone = { map: new Map(map), ...rest };
5044
5044
  clone.map.forEach((value, key) => {
5045
- if (value.equivSet && key === Array.from(value.equivSet)[0]) {
5046
- const equivSet = new Set(value.equivSet);
5047
- equivSet.forEach((k) => clone.map.set(k, { ...clone.map.get(k), equivSet }));
5045
+ if (value.equivSet) {
5046
+ if (key === Array.from(value.equivSet)[0]) {
5047
+ const equivSet = new Set(value.equivSet);
5048
+ equivSet.forEach((k) => clone.map.set(k, { ...clone.map.get(k), equivSet }));
5049
+ }
5050
+ }
5051
+ else {
5052
+ clone.map.set(key, { ...value });
5048
5053
  }
5049
5054
  });
5050
5055
  if (trackedMemberDecls) {
@@ -5081,12 +5086,9 @@ function addCopyPropEvent(blockState, item) {
5081
5086
  const liveCopyPropEvents = blockState.liveCopyPropEvents;
5082
5087
  const decl = item.event.decl;
5083
5088
  assert(declIsLocal(decl));
5084
- let tov = blockState.map.get(decl);
5089
+ const tov = blockState.map.get(decl);
5085
5090
  assert(tov);
5086
- if (tov.copyPropItem !== item) {
5087
- tov = { ...tov, copyPropItem: item };
5088
- blockState.map.set(decl, tov);
5089
- }
5091
+ tov.copyPropItem = item;
5090
5092
  item.contained.forEach((value, key) => {
5091
5093
  const decls = liveCopyPropEvents.get(key);
5092
5094
  if (!decls) {
@@ -5119,15 +5121,13 @@ function clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp) {
5119
5121
  blockState.liveCopyPropEvents
5120
5122
  ?.get(decl)
5121
5123
  ?.forEach((cpDecl) => {
5122
- let value = blockState.map.get(cpDecl);
5124
+ const value = blockState.map.get(cpDecl);
5123
5125
  assert(value && value.copyPropItem);
5124
5126
  assert(Array.from(value.copyPropItem.contained).some(([key]) => {
5125
5127
  return decl === key;
5126
5128
  }));
5127
5129
  copyPropFailed(blockState, value.copyPropItem, nodeCopyProp);
5128
- value = { ...value };
5129
5130
  delete value.copyPropItem;
5130
- blockState.map.set(cpDecl, value);
5131
5131
  });
5132
5132
  }
5133
5133
  function validateTypeState(curState) {
@@ -5170,11 +5170,9 @@ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5170
5170
  if (tov.equivSet) {
5171
5171
  if (intersectEquiv(to.map, from.map, k)) {
5172
5172
  changes = true;
5173
- tov = to.map.get(k);
5174
5173
  }
5175
5174
  }
5176
5175
  if (tov.assocPaths) {
5177
- tov = { ...tov };
5178
5176
  if (!fromv.assocPaths) {
5179
5177
  changes = true;
5180
5178
  delete tov.assocPaths;
@@ -5197,7 +5195,6 @@ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5197
5195
  delete tov.assocPaths;
5198
5196
  }
5199
5197
  }
5200
- to.map.set(k, tov);
5201
5198
  }
5202
5199
  // if both from and to have copyPropEvents, we can only
5203
5200
  // keep it if they're the same event.
@@ -5215,10 +5212,8 @@ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5215
5212
  }
5216
5213
  nodeCopyProp.set(fromv.copyPropItem.event.node, false);
5217
5214
  }
5218
- tov = { ...tov };
5219
5215
  delete tov.copyPropItem;
5220
5216
  changes = true;
5221
- to.map.set(k, tov);
5222
5217
  }
5223
5218
  else {
5224
5219
  assert(k === tov.copyPropItem.event.decl);
@@ -5229,7 +5224,7 @@ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5229
5224
  if (subtypeOf(fromv.curType, tov.curType))
5230
5225
  return;
5231
5226
  if (subtypeOf(tov.curType, fromv.curType)) {
5232
- to.map.set(k, { ...tov, curType: fromv.curType });
5227
+ tov.curType = fromv.curType;
5233
5228
  changes = true;
5234
5229
  return;
5235
5230
  }
@@ -5242,7 +5237,7 @@ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5242
5237
  if (wide)
5243
5238
  result = wide;
5244
5239
  }
5245
- to.map.set(k, { ...tov, curType: result });
5240
+ tov.curType = result;
5246
5241
  changes = true;
5247
5242
  });
5248
5243
  return changes;
@@ -5267,7 +5262,6 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5267
5262
  let droppedComponents = null;
5268
5263
  const entry = blockState.map.get(key);
5269
5264
  assert(entry && entry.assocPaths);
5270
- let newEntry = entry;
5271
5265
  entry.assocPaths.forEach((path) => {
5272
5266
  if (key === baseDecl && path === assignedPath) {
5273
5267
  return;
@@ -5286,12 +5280,9 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5286
5280
  });
5287
5281
  if (pathItem === affectedName && couldBeShallow(type, objectType)) {
5288
5282
  const newAssocKey = assocPath.map((av) => av.name ?? "*").join(".");
5289
- if (newEntry === entry) {
5290
- newEntry = { ...entry };
5291
- }
5292
5283
  if (newAssocKey !== path) {
5293
- newEntry.assocPaths = new Set(newEntry.assocPaths);
5294
- newEntry.assocPaths.delete(path);
5284
+ entry.assocPaths = new Set(entry.assocPaths);
5285
+ entry.assocPaths.delete(path);
5295
5286
  // the "extra" path components will also have entries
5296
5287
  // in blockState.trackedMemberDecls. Since they're gone
5297
5288
  // from here, we (may) need to remove them from there
@@ -5303,8 +5294,7 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5303
5294
  }
5304
5295
  break;
5305
5296
  }
5306
- const baseType = updateByAssocPath(assocPath, assignedType, true);
5307
- newEntry.curType = baseType;
5297
+ entry.curType = updateByAssocPath(assocPath, assignedType, true);
5308
5298
  break;
5309
5299
  }
5310
5300
  if (pathItem === "*") {
@@ -5334,14 +5324,11 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5334
5324
  }
5335
5325
  }
5336
5326
  });
5337
- if (newEntry !== entry) {
5338
- if (droppedComponents) {
5339
- newEntry.assocPaths.forEach((path) => path
5340
- .split(".")
5341
- .forEach((pathComponent) => droppedComponents.delete(pathComponent)));
5342
- droppedComponents.forEach((pathComponent) => blockState.trackedMemberDecls.get(pathComponent).delete(key));
5343
- }
5344
- blockState.map.set(key, newEntry);
5327
+ if (droppedComponents) {
5328
+ entry.assocPaths.forEach((path) => path
5329
+ .split(".")
5330
+ .forEach((pathComponent) => droppedComponents.delete(pathComponent)));
5331
+ droppedComponents.forEach((pathComponent) => blockState.trackedMemberDecls.get(pathComponent).delete(key));
5345
5332
  }
5346
5333
  });
5347
5334
  }
@@ -5448,10 +5435,9 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
5448
5435
  const assocKey = assocValue.map((av) => av.name ?? "*").join(".");
5449
5436
  const newType = updateByAssocPath(assocValue, next, false);
5450
5437
  setStateEvent(blockState, decl.base, newType, newValue ? 1 /* UpdateKind.Inner */ : 0 /* UpdateKind.None */);
5451
- const tsv = { ...blockState.map.get(decl.base) };
5438
+ const tsv = blockState.map.get(decl.base);
5452
5439
  tsv.assocPaths = new Set(tsv.assocPaths);
5453
5440
  tsv.assocPaths.add(assocKey);
5454
- blockState.map.set(decl.base, tsv);
5455
5441
  addTrackedMemberDecl(blockState, decl.base, assocKey);
5456
5442
  if (newValue) {
5457
5443
  const baseElem = assocValue[decl.path.length - 1];
@@ -5529,34 +5515,39 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
5529
5515
  // that foo is side-effect free, and accesses no globals.
5530
5516
  clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp);
5531
5517
  }
5518
+ const v = blockState.map.get(decl);
5519
+ if (!v) {
5520
+ blockState.map.set(decl, { curType: value });
5521
+ return;
5522
+ }
5532
5523
  if (updateKind !== 2 /* UpdateKind.Reassign */) {
5533
5524
  /*
5534
5525
  * If we're not re-assigning, the equivalencies don't
5535
5526
  * change, so this update must be applied to every
5536
5527
  * element of the set
5537
5528
  */
5538
- const v = blockState.map.get(decl);
5539
- if (v?.equivSet) {
5529
+ if (v.equivSet) {
5540
5530
  v.equivSet.forEach((s) => {
5541
5531
  const next = blockState.map.get(s);
5542
5532
  assert(next && next.equivSet?.has(s));
5543
- blockState.map.set(s, { ...next, curType: value });
5533
+ next.curType = value;
5544
5534
  });
5545
5535
  }
5546
5536
  else {
5547
- blockState.map.set(decl, { ...v, curType: value });
5537
+ v.curType = value;
5548
5538
  }
5549
5539
  }
5550
5540
  else {
5551
- const v = blockState.map.get(decl);
5552
5541
  removeEquiv(blockState.map, decl);
5553
- if (v?.assocPaths?.size) {
5542
+ if (v.assocPaths?.size) {
5554
5543
  clearAssocPaths(blockState, decl, v);
5544
+ delete v.assocPaths;
5555
5545
  }
5556
- if (v?.copyPropItem) {
5546
+ if (v.copyPropItem) {
5557
5547
  copyPropFailed(blockState, v.copyPropItem, nodeCopyProp);
5548
+ delete v.copyPropItem;
5558
5549
  }
5559
- blockState.map.set(decl, { curType: value });
5550
+ v.curType = value;
5560
5551
  }
5561
5552
  return;
5562
5553
  }
@@ -5890,7 +5881,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
5890
5881
  break;
5891
5882
  }
5892
5883
  case "ref": {
5893
- let curEntry = getStateEntry(curState, event.decl);
5884
+ const curEntry = getStateEntry(curState, event.decl);
5894
5885
  typeMap.set(event.node, curEntry.curType);
5895
5886
  nodeEquivs.delete(event.node);
5896
5887
  if (curEntry.equivSet) {
@@ -5914,9 +5905,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
5914
5905
  nodeCopyProp.set(curEntry.copyPropItem.event.node, event.node);
5915
5906
  }
5916
5907
  clearCopyProp(curState, curEntry.copyPropItem);
5917
- curEntry = { ...curEntry };
5918
5908
  delete curEntry.copyPropItem;
5919
- curState.map.set(event.decl, curEntry);
5920
5909
  }
5921
5910
  else if (declIsNonLocal(event.decl)) {
5922
5911
  clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
@@ -5991,10 +5980,9 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
5991
5980
  setUnionComponent(type, 32768 /* TypeTag.Object */, newData);
5992
5981
  if (tsv.assocPaths) {
5993
5982
  clearAssocPaths(curState, decl, tsv);
5994
- tsv = { ...tsv };
5995
5983
  delete tsv.assocPaths;
5996
5984
  }
5997
- curState.map.set(decl, { ...tsv, curType: type });
5985
+ tsv.curType = type;
5998
5986
  }
5999
5987
  }
6000
5988
  clearRelatedCopyPropEvents(curState, decl, nodeCopyProp);
@@ -6019,10 +6007,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStore
6019
6007
  (event.node.type !== "AssignmentExpression" ||
6020
6008
  event.node.operator !== "=")) {
6021
6009
  copyPropFailed(curState, before.copyPropItem, nodeCopyProp);
6022
- const v = { ...before };
6023
- delete v.copyPropItem;
6024
- assert(isTypeStateKey(event.decl));
6025
- curState.map.set(event.decl, v);
6010
+ delete before.copyPropItem;
6026
6011
  }
6027
6012
  }
6028
6013
  const expr = event.node.type === "VariableDeclarator"
@@ -6717,7 +6702,9 @@ function couldBeObj(a, b) {
6717
6702
  /* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
6718
6703
  /* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8180);
6719
6704
  /* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(333);
6720
- /* harmony import */ var _type_flow_util__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1638);
6705
+ /* harmony import */ var _minimize_locals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(736);
6706
+ /* harmony import */ var _type_flow_util__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(1638);
6707
+
6721
6708
 
6722
6709
 
6723
6710
 
@@ -6985,7 +6972,10 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
6985
6972
  console.log(`${formatAst(dead)} (${sourceLocation(dead.loc)})`));
6986
6973
  }
6987
6974
  let changes = false;
6988
- traverseAst(func.node.body, null, (node, parent) => {
6975
+ traverseAst(func.node.body, null, (node) => {
6976
+ const cleaned = variableCleanup(node);
6977
+ if (cleaned !== null)
6978
+ return cleaned;
6989
6979
  if (node.type === "ExpressionStatement" &&
6990
6980
  node.expression.type === "AssignmentExpression" &&
6991
6981
  deadStores.has(node.expression)) {
@@ -7007,40 +6997,17 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
7007
6997
  changes = true;
7008
6998
  return { type: "Literal", value: null, raw: "null" };
7009
6999
  }
7010
- if (node.type === "VariableDeclaration") {
7011
- const result = [];
7012
- for (let i = 0; i < node.declarations.length; i++) {
7013
- const decl = node.declarations[i];
7014
- if (decl.init && deadStores.has(decl)) {
7015
- const body = unused(state, decl.init);
7016
- if (body.length) {
7017
- if (!parent ||
7018
- (parent.type !== "BlockStatement" && parent.type !== "SwitchCase")) {
7019
- // Must be the init in a for statement. Fixing
7020
- // it would be complicated, so just punt for now.
7021
- break;
7022
- }
7023
- const newDeclaration = withLoc({ ...node }, node, decl.id);
7024
- if (i + 1 < node.declarations.length) {
7025
- newDeclaration.declarations = node.declarations.splice(0, i + 1);
7026
- result.push(newDeclaration);
7027
- withLoc(node, node.declarations[0], node);
7028
- i = -1;
7029
- }
7030
- else {
7031
- result.push(node);
7032
- }
7033
- result.push(...body);
7034
- }
7035
- changes = true;
7036
- delete decl.init;
7037
- }
7038
- }
7039
- if (result.length) {
7040
- if (!result.includes(node)) {
7041
- result.push(node);
7000
+ if (node.type === "VariableDeclarator") {
7001
+ const decl = node;
7002
+ if (decl.init && deadStores.has(decl)) {
7003
+ const body = unused(state, decl.init);
7004
+ delete decl.init;
7005
+ changes = true;
7006
+ if (!body.length) {
7007
+ return null;
7042
7008
  }
7043
- return result;
7009
+ body.unshift(decl);
7010
+ return body;
7044
7011
  }
7045
7012
  }
7046
7013
  return null;
@@ -9339,7 +9306,7 @@ function restrictByEquality(a, b) {
9339
9306
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9340
9307
 
9341
9308
  "use strict";
9342
- /* unused harmony export minimizeLocals */
9309
+ /* unused harmony exports minimizeLocals, variableCleanup */
9343
9310
  /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4143);
9344
9311
  /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_assert__WEBPACK_IMPORTED_MODULE_0__);
9345
9312
  /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6817);
@@ -9513,11 +9480,6 @@ function minimizeLocals(state, func) {
9513
9480
  return null;
9514
9481
  }
9515
9482
  break;
9516
- case "ExpressionStatement":
9517
- if (node.expression.type === "Literal") {
9518
- return false;
9519
- }
9520
- break;
9521
9483
  case "UpdateExpression":
9522
9484
  if (info) {
9523
9485
  assert(node.argument.type === "Identifier");
@@ -9538,7 +9500,7 @@ function minimizeLocals(state, func) {
9538
9500
  }
9539
9501
  // VariableDeclarations aren't allowed to have
9540
9502
  // AssignmentExpressions in them, but we'll fix that
9541
- // below
9503
+ // via variableCleanup
9542
9504
  return withLoc({
9543
9505
  type: "AssignmentExpression",
9544
9506
  operator: "=",
@@ -9551,79 +9513,297 @@ function minimizeLocals(state, func) {
9551
9513
  }, node, node);
9552
9514
  }
9553
9515
  break;
9554
- case "VariableDeclaration":
9555
- if (node.declarations.some((decl) => decl.type !== "VariableDeclarator")) {
9556
- const results = [];
9557
- node.declarations.forEach((decl) => {
9558
- if (isExpression(decl)) {
9559
- results.push(withLoc({ type: "ExpressionStatement", expression: decl }, decl, decl));
9560
- }
9561
- else if (decl.init) {
9562
- results.push(withLoc({
9563
- type: "ExpressionStatement",
9564
- expression: withLoc({
9565
- type: "AssignmentExpression",
9566
- operator: "=",
9567
- left: withLoc({
9568
- type: "Identifier",
9569
- name: variableDeclarationName(decl.id),
9570
- }, decl.id, decl.id),
9571
- right: decl.init,
9572
- }, decl, decl),
9573
- }, decl, decl));
9574
- }
9575
- });
9576
- node.declarations = node.declarations.filter((decl) => {
9577
- if (decl.type === "VariableDeclarator") {
9578
- delete decl.init;
9579
- return true;
9580
- }
9581
- return false;
9582
- });
9583
- if (node.declarations.length) {
9584
- withLocDeep(node, node, false);
9585
- results.unshift(node);
9516
+ }
9517
+ assert(!info);
9518
+ return variableCleanup(node);
9519
+ });
9520
+ return;
9521
+ }
9522
+ function variableCleanup(node) {
9523
+ switch (node.type) {
9524
+ case "ExpressionStatement":
9525
+ if (node.expression.type === "Literal") {
9526
+ return false;
9527
+ }
9528
+ break;
9529
+ case "VariableDeclaration":
9530
+ if (node.declarations.some((decl) => decl.type !== "VariableDeclarator")) {
9531
+ const results = [];
9532
+ node.declarations.forEach((decl) => {
9533
+ if (isStatement(decl)) {
9534
+ results.push(decl);
9535
+ }
9536
+ else if (isExpression(decl)) {
9537
+ results.push(withLoc({ type: "ExpressionStatement", expression: decl }, decl, decl));
9538
+ }
9539
+ else if (decl.init) {
9540
+ results.push(withLoc({
9541
+ type: "ExpressionStatement",
9542
+ expression: withLoc({
9543
+ type: "AssignmentExpression",
9544
+ operator: "=",
9545
+ left: withLoc({
9546
+ type: "Identifier",
9547
+ name: variableDeclarationName(decl.id),
9548
+ }, decl.id, decl.id),
9549
+ right: decl.init,
9550
+ }, decl, decl),
9551
+ }, decl, decl));
9586
9552
  }
9587
- // if this was the init of a ForStatement, this will
9588
- // replace its init with a BlockStatement, so we have to
9589
- // fix that below.
9590
- return results;
9553
+ });
9554
+ node.declarations = node.declarations.filter((decl) => {
9555
+ if (decl.type === "VariableDeclarator") {
9556
+ delete decl.init;
9557
+ return true;
9558
+ }
9559
+ return false;
9560
+ });
9561
+ if (node.declarations.length) {
9562
+ withLocDeep(node, node, false);
9563
+ results.unshift(node);
9591
9564
  }
9592
- break;
9593
- case "ForStatement":
9594
- if (node.init) {
9595
- if (node.init.type === "BlockStatement") {
9596
- const result = node.init;
9597
- delete node.init;
9598
- result.body.push(node);
9599
- if (node.loc && result.loc) {
9600
- // result has the range of the original VariableDeclaration
9601
- // but now we're moving that ahead of the 'for', so to keep
9602
- // things straight, we need to set the for's start to be
9603
- // where result ended, and result's end to be where the for
9604
- // ends (since that block now encloses the for)
9605
- node.start = result.end;
9606
- node.loc.start = result.loc.end;
9607
- result.end = node.end;
9608
- result.loc.end = node.loc.end;
9565
+ // if this was the init of a ForStatement, this will
9566
+ // replace its init with a BlockStatement, so we have to
9567
+ // fix that below.
9568
+ return results;
9569
+ }
9570
+ break;
9571
+ case "ForStatement":
9572
+ if (node.init) {
9573
+ if (node.init.type === "BlockStatement") {
9574
+ const result = node.init;
9575
+ delete node.init;
9576
+ result.body.push(node);
9577
+ if (node.loc && result.loc) {
9578
+ // result has the range of the original VariableDeclaration
9579
+ // but now we're moving that ahead of the 'for', so to keep
9580
+ // things straight, we need to set the for's start to be
9581
+ // where result ended, and result's end to be where the for
9582
+ // ends (since that block now encloses the for)
9583
+ node.start = result.end;
9584
+ node.loc.start = result.loc.end;
9585
+ result.end = node.end;
9586
+ result.loc.end = node.loc.end;
9587
+ }
9588
+ return result;
9589
+ }
9590
+ if (node.init.type === "Literal") {
9591
+ delete node.init;
9592
+ }
9593
+ }
9594
+ break;
9595
+ case "SequenceExpression":
9596
+ if (node.expressions.some((e) => e.type === "Literal")) {
9597
+ node.expressions = node.expressions.filter((e) => e.type !== "Literal");
9598
+ }
9599
+ break;
9600
+ }
9601
+ return null;
9602
+ }
9603
+
9604
+
9605
+ /***/ }),
9606
+
9607
+ /***/ 4416:
9608
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9609
+
9610
+ "use strict";
9611
+ /* unused harmony export minimizeModules */
9612
+ /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4143);
9613
+ /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_assert__WEBPACK_IMPORTED_MODULE_0__);
9614
+ /* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
9615
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6817);
9616
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_2__);
9617
+
9618
+
9619
+
9620
+ function minimizeModules(ast, state) {
9621
+ const { pre, post } = state;
9622
+ try {
9623
+ const replacementMap = new Map();
9624
+ const conflictingNames = new Set();
9625
+ state.pre = (node) => {
9626
+ if (state.inType)
9627
+ return null;
9628
+ switch (node.type) {
9629
+ case "ModuleDeclaration":
9630
+ case "ClassDeclaration":
9631
+ case "FunctionDeclaration":
9632
+ return ["body"];
9633
+ case "Using":
9634
+ conflictingNames.add(node.as?.name ??
9635
+ (node.id.type === "Identifier"
9636
+ ? node.id.name
9637
+ : node.id.property.name));
9638
+ return [];
9639
+ case "ImportModule":
9640
+ conflictingNames.add(node.id.type === "Identifier" ? node.id.name : node.id.property.name);
9641
+ return [];
9642
+ case "Identifier":
9643
+ case "MemberExpression": {
9644
+ let current = node;
9645
+ const parts = [];
9646
+ while (current.type === "MemberExpression" && !current.computed) {
9647
+ parts.unshift(current);
9648
+ current = current.object;
9649
+ }
9650
+ if (current.type !== "Identifier" &&
9651
+ current.type !== "ThisExpression") {
9652
+ break;
9653
+ }
9654
+ let toReplace = null;
9655
+ let module = null;
9656
+ let addImport = false;
9657
+ let [, results] = state.lookupValue(current, null);
9658
+ let i = 0;
9659
+ for (; results &&
9660
+ results.length === 1 &&
9661
+ results[0].results.length === 1 &&
9662
+ (results[0].results[0].type === "Program" ||
9663
+ results[0].results[0].type === "ClassDeclaration" ||
9664
+ results[0].results[0].type === "ModuleDeclaration"); i++) {
9665
+ if (current.type === "Identifier" &&
9666
+ results[0].results[0].type === "ModuleDeclaration" &&
9667
+ isImportCandidate(results[0].results[0])) {
9668
+ const directResults = i
9669
+ ? state.lookupValue(current, null)
9670
+ : results;
9671
+ if (directResults &&
9672
+ directResults.length === 1 &&
9673
+ directResults[0].results.length === 1 &&
9674
+ directResults[0].results[0] === results[0].results[0]) {
9675
+ // we would find the same thing if we just looked up
9676
+ // current directly.
9677
+ toReplace = (i ? parts[i - 1] : current);
9678
+ module = results[0].results[0];
9679
+ if (findUsingForNode(state, state.stack, state.stack.length - 1, current) === directResults[0].results[0]) {
9680
+ // we already find it via an import, so we don't need
9681
+ // a new import.
9682
+ addImport = false;
9683
+ }
9684
+ else {
9685
+ addImport = true;
9686
+ }
9687
+ }
9688
+ else {
9689
+ toReplace = parts[i - 1];
9690
+ module = results[0].results[0];
9691
+ addImport = true;
9692
+ }
9609
9693
  }
9610
- return result;
9694
+ if (i === parts.length)
9695
+ break;
9696
+ current = parts[i].property;
9697
+ results = lookupNext(state, results, "decls", current);
9698
+ }
9699
+ if (toReplace) {
9700
+ assert(module);
9701
+ replacementMap.set(toReplace, { module, addImport });
9702
+ }
9703
+ else if (parts.length === 0) {
9704
+ assert(node.type === "Identifier");
9705
+ conflictingNames.add(node.name);
9611
9706
  }
9612
- if (node.init.type === "Literal") {
9613
- delete node.init;
9707
+ else if (parts[0].object.type === "Identifier") {
9708
+ conflictingNames.add(parts[0].object.name);
9614
9709
  }
9710
+ return [];
9615
9711
  }
9616
- break;
9617
- case "SequenceExpression":
9618
- if (node.expressions.some((e) => e.type === "Literal")) {
9619
- node.expressions = node.expressions.filter((e) => e.type !== "Literal");
9712
+ }
9713
+ return null;
9714
+ };
9715
+ collectNamespaces(ast, state);
9716
+ const mappedNames = new Map();
9717
+ replacementMap.forEach((value, key) => {
9718
+ let name;
9719
+ if (value.addImport) {
9720
+ name = mappedNames.get(value.module);
9721
+ if (!name) {
9722
+ name = value.module.name;
9723
+ for (let i = 0; conflictingNames.has(name); i++) {
9724
+ name = `${value.module.name}_${i}`;
9725
+ }
9726
+ mappedNames.set(value.module, name);
9727
+ conflictingNames.add(name);
9620
9728
  }
9621
- break;
9622
- }
9623
- assert(!info);
9624
- return null;
9729
+ }
9730
+ else {
9731
+ name = key.type === "Identifier" ? key.name : key.property.name;
9732
+ }
9733
+ const original = formatAstLongLines(key);
9734
+ const repl = key;
9735
+ repl.type = "Identifier";
9736
+ repl.name = name;
9737
+ if (name !== original) {
9738
+ repl.original = original;
9739
+ }
9740
+ delete repl.property;
9741
+ delete repl.object;
9742
+ delete repl.computed;
9743
+ });
9744
+ mappedNames.forEach((name, module) => {
9745
+ const id = makeScopedName(module.fullName.slice(2));
9746
+ const as = name !== (id.type === "Identifier" ? id : id.property).name &&
9747
+ makeIdentifier(name);
9748
+ const using = withLocDeep(as ? { type: "Using", id, as } : { type: "Using", id }, ast, false, true);
9749
+ ast.body.unshift(using);
9750
+ });
9751
+ }
9752
+ finally {
9753
+ state.pre = pre;
9754
+ state.post = post;
9755
+ }
9756
+ }
9757
+ /**
9758
+ * There's a bug in garmin's runtime (although the compiler could
9759
+ * work around it). See
9760
+ *
9761
+ * https://forums.garmin.com/developer/connect-iq/i/bug-reports/referencing-an-imported-module-doesn-t-run-its-parent-s-init
9762
+ *
9763
+ * What this means is that if a module's parent isn't `globals`
9764
+ * and it needs to be initialized, then we can't risk
9765
+ * importing it, because the parent module might not get initialized
9766
+ * in time.
9767
+ */
9768
+ function isImportCandidate(module) {
9769
+ if (module.fullName.startsWith("$.Toybox."))
9770
+ return true;
9771
+ if (module.fullName.startsWith("$.Rez."))
9772
+ return false;
9773
+ assert(module.stack);
9774
+ if (module.stack.length === 1)
9775
+ return true;
9776
+ return module.stack.every((elem) => {
9777
+ if (!elem.sn.decls)
9778
+ return false;
9779
+ if (elem.sn.type === "Program")
9780
+ return true;
9781
+ if (elem.sn.type !== "ModuleDeclaration")
9782
+ return false;
9783
+ return Object.values(elem.sn.decls).every((decls) => decls.every((decl) => {
9784
+ if (decl.type !== "VariableDeclarator")
9785
+ return true;
9786
+ if (!decl.node.init)
9787
+ return true;
9788
+ if (decl.node.init.type === "UnaryExpression" &&
9789
+ decl.node.init.operator === ":") {
9790
+ return true;
9791
+ }
9792
+ if (decl.node.init.type !== "Literal")
9793
+ return false;
9794
+ switch (typeof decl.node.init.value) {
9795
+ case "boolean":
9796
+ return true;
9797
+ case "bigint":
9798
+ return false;
9799
+ case "number":
9800
+ return !/[dl]$/i.test(decl.node.init.raw);
9801
+ case "object":
9802
+ return decl.node.init.value === null;
9803
+ }
9804
+ return false;
9805
+ }));
9625
9806
  });
9626
- return;
9627
9807
  }
9628
9808
 
9629
9809