@markw65/monkeyc-optimizer 1.1.10 → 1.1.11

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
@@ -619,7 +619,7 @@ class LocalState {
619
619
  this.unreachable = true;
620
620
  }
621
621
  }
622
- function buildReducedGraph(state, func, notice) {
622
+ function buildReducedGraph(state, func, refsForUpdate, notice) {
623
623
  const { stack, pre, post } = state;
624
624
  try {
625
625
  const localState = new LocalState(func.node);
@@ -627,8 +627,11 @@ function buildReducedGraph(state, func, notice) {
627
627
  state.stack = [...func.stack];
628
628
  const stmtStack = [func.node];
629
629
  const testStack = [{ node: func.node }];
630
+ const allEvents = [];
631
+ const eventsStack = [];
630
632
  let tryActive = 0;
631
633
  state.pre = (node) => {
634
+ eventsStack.push(allEvents.length);
632
635
  if (state.inType || localState.unreachable) {
633
636
  return [];
634
637
  }
@@ -954,6 +957,12 @@ function buildReducedGraph(state, func, notice) {
954
957
  }
955
958
  return [];
956
959
  case "AssignmentExpression":
960
+ if (refsForUpdate && node.operator !== "=") {
961
+ // if its an update, we need to see a "ref"
962
+ // of the lhs, then whatever happens on the rhs,
963
+ // and then the assignment itself
964
+ return null;
965
+ }
957
966
  if (node.left.type === "MemberExpression") {
958
967
  state.traverse(node.left.object);
959
968
  if (node.left.computed) {
@@ -983,6 +992,7 @@ function buildReducedGraph(state, func, notice) {
983
992
  return null;
984
993
  };
985
994
  const addEvent = (block, event) => {
995
+ allEvents.push(event);
986
996
  if (!block.events) {
987
997
  block.events = [event];
988
998
  }
@@ -991,11 +1001,13 @@ function buildReducedGraph(state, func, notice) {
991
1001
  }
992
1002
  };
993
1003
  state.post = (node) => {
1004
+ const eventIndex = eventsStack.pop();
1005
+ const getContainedEvents = () => allEvents.slice(eventIndex);
994
1006
  const curStmt = stmtStack[stmtStack.length - 1];
995
1007
  const topTest = testStack[testStack.length - 1];
996
1008
  if (!state.inType) {
997
1009
  const throws = tryActive > 0 && mayThrow(node);
998
- const events = notice(node, curStmt, throws);
1010
+ const events = notice(node, curStmt, throws, getContainedEvents);
999
1011
  if (throws) {
1000
1012
  if (!events) {
1001
1013
  throw new Error("mayThrow expression in try/catch must generate an event");
@@ -1042,7 +1054,7 @@ function buildReducedGraph(state, func, notice) {
1042
1054
  throw new Error("Internal error: Unexpected successor edges");
1043
1055
  }
1044
1056
  localState.addEdge(localState.curBlock, topTest.false);
1045
- const event = notice(node, curStmt, 1);
1057
+ const event = notice(node, curStmt, 1, getContainedEvents);
1046
1058
  if (event) {
1047
1059
  if (Array.isArray(event)) {
1048
1060
  throw new Error(`Unexpected array of flw events`);
@@ -1183,8 +1195,10 @@ function getPreOrder(head) {
1183
1195
  /* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6652);
1184
1196
  /* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
1185
1197
  /* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(819);
1186
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6906);
1187
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_5__);
1198
+ /* harmony import */ var _type_flow_type_flow_util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1638);
1199
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(6906);
1200
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_6__);
1201
+
1188
1202
 
1189
1203
 
1190
1204
 
@@ -1349,7 +1363,7 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
1349
1363
  };
1350
1364
  return {
1351
1365
  identifiers,
1352
- graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
1366
+ graph: buildReducedGraph(state, func, wantsAllRefs, (node, stmt, mayThrow, getContainedEvents) => {
1353
1367
  if (mayThrow === 1) {
1354
1368
  return wantsAllRefs ? getFlowEvent(node, stmt, findDecl) : null;
1355
1369
  }
@@ -1473,6 +1487,12 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
1473
1487
  if (rhs)
1474
1488
  def.rhs = rhs;
1475
1489
  }
1490
+ if (declIsLocal(decl)) {
1491
+ const contained = getContainedEvents().filter((e) => e.type === "ref" || e.type === "mod");
1492
+ if (contained.length) {
1493
+ def.containedEvents = contained;
1494
+ }
1495
+ }
1476
1496
  return def;
1477
1497
  }
1478
1498
  break;
@@ -1487,13 +1507,19 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
1487
1507
  decl,
1488
1508
  mayThrow,
1489
1509
  };
1490
- if (wantsAllRefs &&
1491
- node.operator === "=" &&
1492
- (node.right.type === "Identifier" ||
1493
- node.right.type === "MemberExpression")) {
1494
- const rhs = findDecl(node.right);
1495
- if (rhs)
1496
- def.rhs = rhs;
1510
+ if (wantsAllRefs && node.operator === "=") {
1511
+ if (node.right.type === "Identifier" ||
1512
+ node.right.type === "MemberExpression") {
1513
+ const rhs = findDecl(node.right);
1514
+ if (rhs)
1515
+ def.rhs = rhs;
1516
+ }
1517
+ if (declIsLocal(decl)) {
1518
+ const contained = getContainedEvents().filter((e) => e.type === "ref" || e.type === "mod");
1519
+ if (contained.length) {
1520
+ def.containedEvents = contained;
1521
+ }
1522
+ }
1497
1523
  }
1498
1524
  return def;
1499
1525
  }
@@ -1546,6 +1572,12 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
1546
1572
  mod.calleeObj = calleeDecl.base;
1547
1573
  }
1548
1574
  }
1575
+ else if (node.callee.type === "MemberExpression") {
1576
+ const calleeObj = findDecl(node.callee.object);
1577
+ if (calleeObj) {
1578
+ mod.calleeObj = calleeObj;
1579
+ }
1580
+ }
1549
1581
  return mod;
1550
1582
  }
1551
1583
  }
@@ -1846,7 +1878,7 @@ function findCalleesByNode(state, callee) {
1846
1878
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1847
1879
 
1848
1880
  "use strict";
1849
- /* unused harmony exports inlinableSubExpression, shouldInline, unused, inlineDiagnostic, inlineFunction, applyTypeIfNeeded */
1881
+ /* unused harmony exports inlinableSubExpression, inlineRequested, shouldInline, unused, inlineDiagnostic, inlineFunction, applyTypeIfNeeded */
1850
1882
  /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
1851
1883
  /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
1852
1884
  /* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
@@ -2533,16 +2565,18 @@ function updateLocationForInline(node, original, context, func) {
2533
2565
  throw new Error("Internal error: Inlined call had no location info");
2534
2566
  }
2535
2567
  traverseAst(node, (node) => {
2536
- if (node.loc &&
2537
- (node.loc.source !== loc.source ||
2538
- node.loc.start.offset > loc.end.offset ||
2539
- node.loc.end.offset <= loc.start.offset)) {
2540
- if (!node.origins) {
2541
- node.origins = [];
2568
+ if (!node.loc ||
2569
+ node.loc.source !== loc.source ||
2570
+ node.loc.start.offset > loc.end.offset ||
2571
+ node.loc.end.offset <= loc.start.offset) {
2572
+ if (node.loc) {
2573
+ if (!node.origins) {
2574
+ node.origins = [];
2575
+ }
2576
+ node.origins.unshift({ loc: node.loc, func: func.fullName });
2542
2577
  }
2543
- node.origins.unshift({ loc: node.loc, func: func.fullName });
2578
+ withLoc(node, context, context === original ? context : false);
2544
2579
  }
2545
- withLoc(node, context, context === original ? context : false);
2546
2580
  });
2547
2581
  return node;
2548
2582
  }
@@ -2570,8 +2604,8 @@ function inlineFunction(state, func, call, context) {
2570
2604
  const ret = inlineFunctionHelper(state, func, call, context);
2571
2605
  if (!ret)
2572
2606
  return ret;
2573
- const typecheckFalse = func.node.attrs?.attributes?.elements.find((attr) => isTypecheckArg(attr, false));
2574
- if (!typecheckFalse) {
2607
+ const typecheckAttrs = func.node.attrs?.attributes?.elements.filter((attr) => isTypecheckArg(attr, null));
2608
+ if (!typecheckAttrs || !typecheckAttrs.length) {
2575
2609
  return ret;
2576
2610
  }
2577
2611
  const callerElem = state.stack.find((elem) => elem.sn.type === "FunctionDeclaration");
@@ -2580,6 +2614,9 @@ function inlineFunction(state, func, call, context) {
2580
2614
  }
2581
2615
  const callerSn = callerElem.sn;
2582
2616
  const caller = callerSn.node;
2617
+ if (caller.attrs?.attributes?.elements.find((attr) => isTypecheckArg(attr, null))) {
2618
+ return ret;
2619
+ }
2583
2620
  if (!caller.attrs) {
2584
2621
  caller.attrs = withLoc({
2585
2622
  type: "AttributeList",
@@ -2588,10 +2625,7 @@ function inlineFunction(state, func, call, context) {
2588
2625
  if (!caller.attrs.attributes) {
2589
2626
  caller.attrs.attributes = withLoc({ type: "Attributes", elements: [] }, caller.attrs, false);
2590
2627
  }
2591
- if (caller.attrs.attributes.elements.find((attr) => isTypecheckArg(attr, null))) {
2592
- return ret;
2593
- }
2594
- caller.attrs.attributes.elements.unshift(withLocDeep({ ...typecheckFalse }, caller.attrs, false));
2628
+ caller.attrs.attributes.elements.unshift(...typecheckAttrs.map((typecheck) => withLocDeep({ ...typecheck }, caller.attrs, false)));
2595
2629
  return ret;
2596
2630
  }
2597
2631
  function applyTypeIfNeeded(node) {
@@ -3177,6 +3211,8 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3177
3211
  state.localsStack = [{}];
3178
3212
  state.calledFunctions = {};
3179
3213
  state.usedByName = {};
3214
+ const checkLookupRules = config.checkCompilerLookupRules;
3215
+ config.checkCompilerLookupRules = "OFF";
3180
3216
  let again = false;
3181
3217
  const optimizeCallHelper = (istate, call, node) => {
3182
3218
  const result = optimizeCall(istate, call, node);
@@ -3749,6 +3785,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3749
3785
  return ret;
3750
3786
  });
3751
3787
  });
3788
+ config.checkCompilerLookupRules = checkLookupRules;
3752
3789
  reportMissingSymbols(state, config);
3753
3790
  if (state.inlineDiagnostics) {
3754
3791
  if (!state.diagnostics) {
@@ -3970,7 +4007,9 @@ function pragmaChecker(state, ast, diagnostics) {
3970
4007
  if (quote === '"') {
3971
4008
  return haystack.includes(needle);
3972
4009
  }
3973
- const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat.replace(/\W/g, "_")}(?:_\\d+)?)`));
4010
+ const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat
4011
+ .replace(/^([a-zA-Z_]+\.)*/, "")
4012
+ .replace(/\W/g, "_")}(?:_\\d+)?)`));
3974
4013
  return re.test(haystack);
3975
4014
  };
3976
4015
  next();
@@ -4167,7 +4206,9 @@ function sizeBasedPRE(state, func) {
4167
4206
  applyReplacements(func.node, nodeMap, declMap);
4168
4207
  func.node.body.body.unshift(variableDecl);
4169
4208
  }
4170
- minimizeLocals(state, func);
4209
+ if (state.config?.minimizeLocals ?? true) {
4210
+ minimizeLocals(state, func);
4211
+ }
4171
4212
  }
4172
4213
  function buildPREGraph(state, func) {
4173
4214
  const result = buildDataFlowGraph(state, func, (literal) => refCost(literal) > LocalRefCost, true, false);
@@ -4825,18 +4866,20 @@ function applyReplacements(func, nodeMap, declMap) {
4825
4866
  /* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
4826
4867
  /* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8180);
4827
4868
  /* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(819);
4828
- /* harmony import */ var _optimizer_types__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9697);
4829
- /* harmony import */ var _type_flow_could_be__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(4055);
4830
- /* harmony import */ var _type_flow_dead_store__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(8486);
4831
- /* harmony import */ var _type_flow_interp__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(7161);
4832
- /* harmony import */ var _type_flow_interp_call__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(2222);
4833
- /* harmony import */ var _type_flow_intersection_type__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(6973);
4834
- /* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(9234);
4835
- /* harmony import */ var _type_flow_type_flow_util__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(1638);
4836
- /* harmony import */ var _type_flow_types__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(7255);
4837
- /* harmony import */ var _type_flow_union_type__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(757);
4838
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(6906);
4839
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_16__);
4869
+ /* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(333);
4870
+ /* harmony import */ var _optimizer_types__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9697);
4871
+ /* harmony import */ var _type_flow_could_be__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(4055);
4872
+ /* harmony import */ var _type_flow_dead_store__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(8486);
4873
+ /* harmony import */ var _type_flow_interp__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(7161);
4874
+ /* harmony import */ var _type_flow_interp_call__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(2222);
4875
+ /* harmony import */ var _type_flow_intersection_type__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(6973);
4876
+ /* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(9234);
4877
+ /* harmony import */ var _type_flow_type_flow_util__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(1638);
4878
+ /* harmony import */ var _type_flow_types__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(7255);
4879
+ /* harmony import */ var _type_flow_union_type__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(757);
4880
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(6906);
4881
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_17___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_17__);
4882
+
4840
4883
 
4841
4884
 
4842
4885
 
@@ -4875,15 +4918,24 @@ function buildTypeInfo(state, func, optimizeEquivalencies) {
4875
4918
  const logThisRun = logging && loggingEnabledFor("TYPEFLOW_FUNC", func);
4876
4919
  while (true) {
4877
4920
  const { graph } = buildDataFlowGraph(state, func, () => false, false, true);
4878
- if (optimizeEquivalencies &&
4879
- eliminateDeadStores(state, func, graph, logThisRun)) {
4880
- /*
4881
- * eliminateDeadStores can change the control flow graph,
4882
- * so we need to recompute it if it did anything.
4883
- */
4884
- continue;
4921
+ let copyPropStores;
4922
+ if (optimizeEquivalencies) {
4923
+ const result = eliminateDeadStores(state, func, graph, logThisRun);
4924
+ if (result.changes) {
4925
+ /*
4926
+ * eliminateDeadStores can change the control flow graph,
4927
+ * so we need to recompute it if it did anything.
4928
+ */
4929
+ continue;
4930
+ }
4931
+ if (result.copyPropStores.size) {
4932
+ copyPropStores = result.copyPropStores;
4933
+ }
4934
+ }
4935
+ const result = propagateTypes({ ...state, stack: func.stack }, func, graph, optimizeEquivalencies, copyPropStores, logThisRun);
4936
+ if (!result.redo) {
4937
+ return result.istate;
4885
4938
  }
4886
- return propagateTypes({ ...state, stack: func.stack }, func, graph, optimizeEquivalencies, logThisRun).istate;
4887
4939
  }
4888
4940
  }
4889
4941
  function buildConflictGraph(state, func) {
@@ -4891,8 +4943,8 @@ function buildConflictGraph(state, func) {
4891
4943
  return;
4892
4944
  const logThisRun = logging && loggingEnabledFor("CONFLICT_FUNC", func);
4893
4945
  const { graph, identifiers } = buildDataFlowGraph(state, func, () => false, false, true);
4894
- const { nodeEquivs } = propagateTypes({ ...state, stack: func.stack }, func, graph, false, false);
4895
- const { locals, localConflicts } = findDeadStores(func, graph, nodeEquivs, logThisRun);
4946
+ const { nodeEquivs } = propagateTypes({ ...state, stack: func.stack }, func, graph, false, undefined, logThisRun);
4947
+ const { locals, localConflicts } = findDeadStores(func, graph, nodeEquivs, false, logThisRun);
4896
4948
  return { graph, localConflicts, locals, identifiers, logThisRun };
4897
4949
  }
4898
4950
  function addEquiv(ts, key, equiv) {
@@ -5012,12 +5064,18 @@ function clearAssocPaths(blockState, decl, v) {
5012
5064
  }
5013
5065
  }
5014
5066
  function cloneTypeState(blockState) {
5015
- const { map, trackedMemberDecls, ...rest } = blockState;
5067
+ const { map, trackedMemberDecls, liveCopyPropEvents, ...rest } = blockState;
5016
5068
  const clone = { map: new Map(map), ...rest };
5017
5069
  if (trackedMemberDecls) {
5018
5070
  clone.trackedMemberDecls = new Map();
5019
5071
  trackedMemberDecls.forEach((value, key) => {
5020
- clone.trackedMemberDecls.set(key, new Map(value));
5072
+ clone.trackedMemberDecls.set(key, new Set(value));
5073
+ });
5074
+ }
5075
+ if (liveCopyPropEvents) {
5076
+ clone.liveCopyPropEvents = new Map();
5077
+ liveCopyPropEvents.forEach((value, key) => {
5078
+ clone.liveCopyPropEvents?.set(key, new Set(value));
5021
5079
  });
5022
5080
  }
5023
5081
  return clone;
@@ -5029,18 +5087,84 @@ function addTrackedMemberDecl(blockState, key, assocKey) {
5029
5087
  assocKey.split(".").forEach((pathItem) => {
5030
5088
  const entries = blockState.trackedMemberDecls.get(pathItem);
5031
5089
  if (!entries) {
5032
- blockState.trackedMemberDecls.set(pathItem, new Map([[key, new Set([assocKey])]]));
5090
+ blockState.trackedMemberDecls.set(pathItem, new Set([key]));
5033
5091
  return;
5034
5092
  }
5035
- const entry = entries.get(key);
5036
- if (!entry) {
5037
- entries.set(key, new Set([assocKey]));
5038
- return;
5093
+ entries.add(key);
5094
+ });
5095
+ }
5096
+ function addCopyPropEvent(blockState, item) {
5097
+ if (!blockState.liveCopyPropEvents) {
5098
+ blockState.liveCopyPropEvents = new Map();
5099
+ }
5100
+ const liveCopyPropEvents = blockState.liveCopyPropEvents;
5101
+ const decl = item.event.decl;
5102
+ assert(declIsLocal(decl));
5103
+ let tov = blockState.map.get(decl);
5104
+ assert(tov);
5105
+ if (tov.copyPropItem !== item) {
5106
+ tov = { ...tov, copyPropItem: item };
5107
+ blockState.map.set(decl, tov);
5108
+ }
5109
+ item.contained.forEach((value, key) => {
5110
+ const decls = liveCopyPropEvents.get(key);
5111
+ if (!decls) {
5112
+ liveCopyPropEvents.set(key, new Set([decl]));
5113
+ }
5114
+ else {
5115
+ decls.add(decl);
5039
5116
  }
5040
- entry.add(assocKey);
5041
5117
  });
5042
5118
  }
5043
- function mergeTypeState(blockStates, index, from) {
5119
+ function clearCopyProp(blockState, item) {
5120
+ const liveCopyPropEvents = blockState.liveCopyPropEvents;
5121
+ assert(liveCopyPropEvents);
5122
+ const itemDecl = item.event.decl;
5123
+ item.contained.forEach((event, decl) => {
5124
+ const decls = liveCopyPropEvents.get(decl);
5125
+ assert(decls && decls.has(itemDecl));
5126
+ decls.delete(itemDecl);
5127
+ });
5128
+ }
5129
+ function copyPropFailed(blockState, item, nodeCopyProp) {
5130
+ clearCopyProp(blockState, item);
5131
+ const ref = nodeCopyProp.get(item.event.node);
5132
+ if (ref) {
5133
+ nodeCopyProp.delete(ref);
5134
+ }
5135
+ nodeCopyProp.set(item.event.node, false);
5136
+ }
5137
+ function clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp) {
5138
+ blockState.liveCopyPropEvents
5139
+ ?.get(decl)
5140
+ ?.forEach((cpDecl) => {
5141
+ let value = blockState.map.get(cpDecl);
5142
+ assert(value && value.copyPropItem);
5143
+ assert(Array.from(value.copyPropItem.contained).some(([key]) => {
5144
+ return decl === key;
5145
+ }));
5146
+ copyPropFailed(blockState, value.copyPropItem, nodeCopyProp);
5147
+ value = { ...value };
5148
+ delete value.copyPropItem;
5149
+ blockState.map.set(cpDecl, value);
5150
+ });
5151
+ }
5152
+ function validateTypeState(curState) {
5153
+ curState.liveCopyPropEvents?.forEach((decls) => decls.forEach((cpDecl) => {
5154
+ const value = curState.map.get(cpDecl);
5155
+ assert(value && value.copyPropItem);
5156
+ }));
5157
+ curState.trackedMemberDecls?.forEach((affected, key) => {
5158
+ affected.forEach((decl) => {
5159
+ const value = curState.map.get(decl);
5160
+ assert(value && value.assocPaths);
5161
+ if (!Array.from(value.assocPaths).some((path) => `.${path}.`.includes(`.${key}.`))) {
5162
+ throw new Error("What");
5163
+ }
5164
+ });
5165
+ });
5166
+ }
5167
+ function mergeTypeState(blockStates, index, from, nodeCopyProp) {
5044
5168
  const to = blockStates[index];
5045
5169
  if (!to) {
5046
5170
  blockStates[index] = cloneTypeState(from);
@@ -5048,9 +5172,9 @@ function mergeTypeState(blockStates, index, from) {
5048
5172
  return true;
5049
5173
  }
5050
5174
  const widen = ++to.visits > 10;
5051
- // we'll rebuild this from scratch via
5052
- // addTrackedMemberDecl below.
5175
+ // we'll rebuild these from scratch
5053
5176
  delete to.trackedMemberDecls;
5177
+ delete to.liveCopyPropEvents;
5054
5178
  let changes = false;
5055
5179
  to.map.forEach((tov, k) => {
5056
5180
  const fromv = from.map.get(k);
@@ -5069,25 +5193,57 @@ function mergeTypeState(blockStates, index, from) {
5069
5193
  }
5070
5194
  }
5071
5195
  if (tov.assocPaths) {
5072
- const assocPaths = new Set(tov.assocPaths);
5073
5196
  tov = { ...tov };
5074
5197
  if (!fromv.assocPaths) {
5075
5198
  changes = true;
5076
5199
  delete tov.assocPaths;
5077
5200
  }
5078
5201
  else {
5202
+ const assocPaths = new Set(tov.assocPaths);
5079
5203
  assocPaths.forEach((key) => {
5080
5204
  if (!fromv.assocPaths.has(key)) {
5081
5205
  assocPaths.delete(key);
5206
+ changes = true;
5082
5207
  }
5083
5208
  else {
5084
5209
  addTrackedMemberDecl(to, k, key);
5085
5210
  }
5086
5211
  });
5087
- tov.assocPaths = assocPaths;
5212
+ if (assocPaths.size) {
5213
+ tov.assocPaths = assocPaths;
5214
+ }
5215
+ else {
5216
+ delete tov.assocPaths;
5217
+ }
5088
5218
  }
5089
5219
  to.map.set(k, tov);
5090
5220
  }
5221
+ // if both from and to have copyPropEvents, we can only
5222
+ // keep it if they're the same event.
5223
+ if (tov.copyPropItem) {
5224
+ if (tov.copyPropItem.event !== fromv.copyPropItem?.event) {
5225
+ const toProp = nodeCopyProp.get(tov.copyPropItem.event.node);
5226
+ if (toProp) {
5227
+ nodeCopyProp.delete(toProp);
5228
+ }
5229
+ nodeCopyProp.set(tov.copyPropItem.event.node, false);
5230
+ if (fromv.copyPropItem) {
5231
+ const fromProp = nodeCopyProp.get(fromv.copyPropItem.event.node);
5232
+ if (fromProp) {
5233
+ nodeCopyProp.delete(fromProp);
5234
+ }
5235
+ nodeCopyProp.set(fromv.copyPropItem.event.node, false);
5236
+ }
5237
+ tov = { ...tov };
5238
+ delete tov.copyPropItem;
5239
+ changes = true;
5240
+ to.map.set(k, tov);
5241
+ }
5242
+ else {
5243
+ assert(k === tov.copyPropItem.event.decl);
5244
+ addCopyPropEvent(to, tov.copyPropItem);
5245
+ }
5246
+ }
5091
5247
  if (widen) {
5092
5248
  if (subtypeOf(fromv.curType, tov.curType))
5093
5249
  return;
@@ -5116,9 +5272,7 @@ function tsEquivs(state, key) {
5116
5272
  do {
5117
5273
  result.push(tsKey(s));
5118
5274
  const next = state.get(s);
5119
- if (!next || !next.equivSet) {
5120
- throw new Error(`Inconsistent equivSet for ${tsKey(key)}: missing value for ${tsKey(s)}`);
5121
- }
5275
+ assert(next && next.equivSet);
5122
5276
  s = next.equivSet.next;
5123
5277
  } while (s !== key);
5124
5278
  return `[(${result.join(", ")})]`;
@@ -5137,17 +5291,20 @@ function printBlockState(block, state, indent = "") {
5137
5291
  });
5138
5292
  }
5139
5293
  function updateAffected(blockState, objectType, baseDecl, assignedPath, affectedName, affected, assignedType) {
5140
- affected.forEach((paths, key) => {
5294
+ affected.forEach((key) => {
5295
+ let droppedComponents = null;
5141
5296
  const entry = blockState.map.get(key);
5142
- assert(entry);
5297
+ assert(entry && entry.assocPaths);
5143
5298
  let newEntry = entry;
5144
- paths.forEach((path) => {
5299
+ entry.assocPaths.forEach((path) => {
5145
5300
  if (key === baseDecl && path === assignedPath) {
5146
5301
  return;
5147
5302
  }
5148
- assert(entry.assocPaths?.has(path));
5149
- const assocPath = [];
5150
5303
  const pathSegments = path.split(".");
5304
+ if (!pathSegments.includes(affectedName)) {
5305
+ return;
5306
+ }
5307
+ const assocPath = [];
5151
5308
  let type = entry.curType;
5152
5309
  for (let i = 0; i < pathSegments.length; i++) {
5153
5310
  const pathItem = pathSegments[i];
@@ -5157,20 +5314,25 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5157
5314
  });
5158
5315
  if (pathItem === affectedName && couldBeShallow(type, objectType)) {
5159
5316
  const newAssocKey = assocPath.map((av) => av.name ?? "*").join(".");
5160
- const baseType = updateByAssocPath(assocPath, assignedType, true);
5161
5317
  if (newEntry === entry) {
5162
5318
  newEntry = { ...entry };
5163
5319
  }
5164
- newEntry.curType = baseType;
5165
- if (path !== newAssocKey) {
5166
- newEntry.assocPaths = new Set(entry.assocPaths);
5320
+ if (newAssocKey !== path) {
5321
+ newEntry.assocPaths = new Set(newEntry.assocPaths);
5167
5322
  newEntry.assocPaths.delete(path);
5168
- newEntry.assocPaths.add(newAssocKey);
5169
- const newPaths = new Set(paths);
5170
- newPaths.delete(path);
5171
- newPaths.add(newAssocKey);
5172
- affected.set(key, newPaths);
5323
+ // the "extra" path components will also have entries
5324
+ // in blockState.trackedMemberDecls. Since they're gone
5325
+ // from here, we (may) need to remove them from there
5326
+ if (!droppedComponents) {
5327
+ droppedComponents = new Set();
5328
+ }
5329
+ while (++i < pathSegments.length) {
5330
+ droppedComponents.add(pathSegments[i]);
5331
+ }
5332
+ break;
5173
5333
  }
5334
+ const baseType = updateByAssocPath(assocPath, assignedType, true);
5335
+ newEntry.curType = baseType;
5174
5336
  break;
5175
5337
  }
5176
5338
  if (pathItem === "*") {
@@ -5201,11 +5363,17 @@ function updateAffected(blockState, objectType, baseDecl, assignedPath, affected
5201
5363
  }
5202
5364
  });
5203
5365
  if (newEntry !== entry) {
5366
+ if (droppedComponents) {
5367
+ newEntry.assocPaths.forEach((path) => path
5368
+ .split(".")
5369
+ .forEach((pathComponent) => droppedComponents.delete(pathComponent)));
5370
+ droppedComponents.forEach((pathComponent) => blockState.trackedMemberDecls.get(pathComponent).delete(key));
5371
+ }
5204
5372
  blockState.map.set(key, newEntry);
5205
5373
  }
5206
5374
  });
5207
5375
  }
5208
- function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5376
+ function propagateTypes(state, func, graph, optimizeEquivalencies, copyPropStores, logThisRun) {
5209
5377
  // We want to traverse the blocks in reverse post order, in
5210
5378
  // order to propagate the "availability" of the types.
5211
5379
  const order = getPostOrder(graph).reverse();
@@ -5227,7 +5395,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5227
5395
  printBlockTrailer(block);
5228
5396
  });
5229
5397
  }
5230
- function memberDeclInfo(blockState, decl, clearEquiv, newValue) {
5398
+ function memberDeclInfo(blockState, decl, newValue) {
5231
5399
  const baseType = getStateType(blockState, decl.base);
5232
5400
  const typePath = [baseType];
5233
5401
  let next = null;
@@ -5307,13 +5475,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5307
5475
  }));
5308
5476
  const assocKey = assocValue.map((av) => av.name ?? "*").join(".");
5309
5477
  const newType = updateByAssocPath(assocValue, next, false);
5310
- setStateEvent(blockState, decl.base, newType, false);
5311
- // setStateEvent guarantees that tsv is "unshared" at this
5312
- // point. So we can munge it directly.
5313
- const tsv = blockState.map.get(decl.base);
5314
- if (!tsv.assocPaths)
5315
- tsv.assocPaths = new Set();
5478
+ setStateEvent(blockState, decl.base, newType, newValue ? 1 /* UpdateKind.Inner */ : 0 /* UpdateKind.None */);
5479
+ const tsv = { ...blockState.map.get(decl.base) };
5480
+ tsv.assocPaths = new Set(tsv.assocPaths);
5316
5481
  tsv.assocPaths.add(assocKey);
5482
+ blockState.map.set(decl.base, tsv);
5317
5483
  addTrackedMemberDecl(blockState, decl.base, assocKey);
5318
5484
  if (newValue) {
5319
5485
  const baseElem = assocValue[decl.path.length - 1];
@@ -5341,7 +5507,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5341
5507
  const doUpdate = (key, cur) => {
5342
5508
  const update = cloneType(cur.curType);
5343
5509
  unionInto(update, next);
5344
- setStateEvent(blockState, key, update, false);
5510
+ setStateEvent(blockState, key, update, 0 /* UpdateKind.None */);
5345
5511
  };
5346
5512
  if (decls.length === 1) {
5347
5513
  const cur = blockState.map.get(decls[0]);
@@ -5375,16 +5541,30 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5375
5541
  return cur;
5376
5542
  }, { type: 0 /* TypeTag.Never */ });
5377
5543
  }
5378
- function setStateEvent(blockState, decl, value, clearEquiv) {
5544
+ function setStateEvent(blockState, decl, value, updateKind) {
5379
5545
  if (Array.isArray(decl) ||
5380
5546
  (decl.type !== "MemberDecl" && decl.type !== "Unknown")) {
5381
- const v = { ...blockState.map.get(decl) };
5382
- if (!clearEquiv) {
5547
+ if (updateKind !== 0 /* UpdateKind.None */) {
5548
+ // even if we're only modifying an Object, rather
5549
+ // than reassigning it, we need to clear the
5550
+ // related copy prop events, because although
5551
+ // we may only see the object itself, the expression
5552
+ // could access its fields. eg if x is the decl, then
5553
+ //
5554
+ // foo(x);
5555
+ //
5556
+ // might change when x.a.b.c is changed, even if we know
5557
+ // that foo is side-effect free, and accesses no globals.
5558
+ clearRelatedCopyPropEvents(blockState, decl, nodeCopyProp);
5559
+ }
5560
+ if (updateKind !== 2 /* UpdateKind.Reassign */) {
5383
5561
  /*
5384
- * If we're not clearing the equivalencies then this update
5385
- * must be applied to every element of the set
5562
+ * If we're not re-assigning, the equivalencies don't
5563
+ * change, so this update must be applied to every
5564
+ * element of the set
5386
5565
  */
5387
- if (v.equivSet) {
5566
+ const v = blockState.map.get(decl);
5567
+ if (v?.equivSet) {
5388
5568
  let s = decl;
5389
5569
  do {
5390
5570
  const next = blockState.map.get(s);
@@ -5394,25 +5574,28 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5394
5574
  blockState.map.set(s, { ...next, curType: value });
5395
5575
  s = next.equivSet.next;
5396
5576
  } while (s !== decl);
5397
- return;
5577
+ }
5578
+ else {
5579
+ blockState.map.set(decl, { ...v, curType: value });
5398
5580
  }
5399
5581
  }
5400
5582
  else {
5583
+ const v = blockState.map.get(decl);
5401
5584
  removeEquiv(blockState.map, decl);
5402
- delete v.equivSet;
5403
- if (v.assocPaths?.size) {
5585
+ if (v?.assocPaths?.size) {
5404
5586
  clearAssocPaths(blockState, decl, v);
5405
- delete v.assocPaths;
5406
5587
  }
5588
+ if (v?.copyPropItem) {
5589
+ copyPropFailed(blockState, v.copyPropItem, nodeCopyProp);
5590
+ }
5591
+ blockState.map.set(decl, { curType: value });
5407
5592
  }
5408
- v.curType = value;
5409
- blockState.map.set(decl, v);
5410
5593
  return;
5411
5594
  }
5412
5595
  if (decl.type === "Unknown") {
5413
5596
  return;
5414
5597
  }
5415
- return memberDeclInfo(blockState, decl, clearEquiv, value)?.[1];
5598
+ return memberDeclInfo(blockState, decl, value)?.[1];
5416
5599
  }
5417
5600
  function getStateType(blockState, decl) {
5418
5601
  return getStateEntry(blockState, decl).curType;
@@ -5430,7 +5613,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5430
5613
  if (decl.type === "Unknown") {
5431
5614
  return { curType: { type: 0 /* TypeTag.Never */ } };
5432
5615
  }
5433
- const info = memberDeclInfo(blockState, decl, false);
5616
+ const info = memberDeclInfo(blockState, decl);
5434
5617
  return { curType: info ? info[0] : { type: 524287 /* TypeTag.Any */ } };
5435
5618
  }
5436
5619
  const blockStates = [];
@@ -5451,23 +5634,58 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5451
5634
  if (succ.order == null) {
5452
5635
  throw new Error("Unreachable block was visited");
5453
5636
  }
5454
- if (mergeTypeState(blockStates, succ.order, curState)) {
5637
+ if (mergeTypeState(blockStates, succ.order, curState, nodeCopyProp)) {
5638
+ if (logThisRun) {
5639
+ console.log(`re-merge: ${top.order} -> ${succ.order}`);
5640
+ }
5455
5641
  queue.enqueue(succ);
5456
5642
  }
5457
5643
  });
5458
5644
  };
5459
- function handleMod(curState, calleeObjDecl, callees, node) {
5645
+ function checkModResults(curState, calleeObjDecl, callees, node) {
5460
5646
  const calleeObj = getStateType(curState, calleeObjDecl);
5461
- return every(callees, (callee) => {
5462
- const info = sysCallInfo(callee);
5647
+ const calleeResult = { type: 0 /* TypeTag.Never */ };
5648
+ const result = every(callees, (callee) => {
5649
+ const info = sysCallInfo(istate.state, callee);
5463
5650
  if (!info)
5464
5651
  return false;
5465
5652
  const result = info(istate.state, callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
5653
+ if (!result.effectFree) {
5654
+ return false;
5655
+ }
5466
5656
  if (result.calleeObj) {
5467
- setStateEvent(curState, calleeObjDecl, result.calleeObj, false);
5657
+ unionInto(calleeResult, result.calleeObj);
5468
5658
  }
5469
5659
  return true;
5470
5660
  });
5661
+ return result ? calleeResult : null;
5662
+ }
5663
+ function modInterference(blockState, event, doUpdate, callback) {
5664
+ let callees = undefined;
5665
+ if (event.calleeDecl) {
5666
+ const calleeType = getStateType(blockState, event.calleeDecl);
5667
+ if (hasValue(calleeType) && calleeType.type === 8192 /* TypeTag.Function */) {
5668
+ callees = calleeType.value;
5669
+ }
5670
+ else {
5671
+ callees = findCalleesByNode(state, event.node);
5672
+ }
5673
+ }
5674
+ if (callees === undefined && event.callees !== undefined) {
5675
+ callees = event.callees;
5676
+ }
5677
+ if (event.calleeObj) {
5678
+ const calleeObjResult = checkModResults(blockState, event.calleeObj, callees, event.node);
5679
+ if (calleeObjResult) {
5680
+ if (calleeObjResult.type !== 0 /* TypeTag.Never */) {
5681
+ if (doUpdate) {
5682
+ setStateEvent(blockState, event.calleeObj, calleeObjResult, 0 /* UpdateKind.None */);
5683
+ }
5684
+ }
5685
+ return true;
5686
+ }
5687
+ }
5688
+ return callback(callees, event.calleeObj);
5471
5689
  }
5472
5690
  function handleFlowEvent(event, top, curState) {
5473
5691
  const fixTypes = (equal, left, right, leftDecl, rightDecl) => {
@@ -5487,7 +5705,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5487
5705
  }
5488
5706
  }
5489
5707
  const tmpState = cloneTypeState(curState);
5490
- setStateEvent(tmpState, leftDecl, leftr, false);
5708
+ setStateEvent(tmpState, leftDecl, leftr, 0 /* UpdateKind.None */);
5491
5709
  if (rightDecl) {
5492
5710
  let rightr = restrictByEquality(left, right);
5493
5711
  if (rightr.type === 0 /* TypeTag.Never */) {
@@ -5499,7 +5717,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5499
5717
  return false;
5500
5718
  }
5501
5719
  }
5502
- setStateEvent(tmpState, rightDecl, rightr, false);
5720
+ setStateEvent(tmpState, rightDecl, rightr, 0 /* UpdateKind.None */);
5503
5721
  }
5504
5722
  return tmpState;
5505
5723
  }
@@ -5513,7 +5731,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5513
5731
  if (singletonRemoved.type === 0 /* TypeTag.Never */)
5514
5732
  return false;
5515
5733
  const tmpState = cloneTypeState(curState);
5516
- setStateEvent(tmpState, leftDecl, singletonRemoved, false);
5734
+ setStateEvent(tmpState, leftDecl, singletonRemoved, 0 /* UpdateKind.None */);
5517
5735
  return tmpState;
5518
5736
  }
5519
5737
  return null;
@@ -5545,7 +5763,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5545
5763
  if (singletonRemoved.type === 0 /* TypeTag.Never */)
5546
5764
  return false;
5547
5765
  const tmpState = cloneTypeState(curState);
5548
- setStateEvent(tmpState, event.left, singletonRemoved, false);
5766
+ setStateEvent(tmpState, event.left, singletonRemoved, 0 /* UpdateKind.None */);
5549
5767
  return tmpState;
5550
5768
  }
5551
5769
  }
@@ -5560,7 +5778,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5560
5778
  if (nonNullRemoved.type === 0 /* TypeTag.Never */)
5561
5779
  return false;
5562
5780
  const tmpState = cloneTypeState(curState);
5563
- setStateEvent(tmpState, event.left, nonNullRemoved, false);
5781
+ setStateEvent(tmpState, event.left, nonNullRemoved, 0 /* UpdateKind.None */);
5564
5782
  return tmpState;
5565
5783
  }
5566
5784
  break;
@@ -5618,7 +5836,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5618
5836
  }
5619
5837
  if (result) {
5620
5838
  const tmpState = cloneTypeState(curState);
5621
- setStateEvent(tmpState, event.left, result, false);
5839
+ setStateEvent(tmpState, event.left, result, 0 /* UpdateKind.None */);
5622
5840
  return tmpState;
5623
5841
  }
5624
5842
  }
@@ -5645,7 +5863,10 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5645
5863
  console.log(` Flow (true): merge to ${trueSucc.order || -1}`);
5646
5864
  printBlockState(top, sTrue || curState, " >true ");
5647
5865
  }
5648
- if (mergeTypeState(blockStates, trueSucc.order, sTrue || curState)) {
5866
+ if (mergeTypeState(blockStates, trueSucc.order, sTrue || curState, nodeCopyProp)) {
5867
+ if (logThisRun) {
5868
+ console.log(`re-merge: ${top.order} -> ${trueSucc.order}`);
5869
+ }
5649
5870
  queue.enqueue(trueSucc);
5650
5871
  }
5651
5872
  }
@@ -5657,22 +5878,33 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5657
5878
  console.log(` Flow (false): merge to: ${falseSucc.order || -1}`);
5658
5879
  printBlockState(top, sFalse || curState, " >false ");
5659
5880
  }
5660
- if (mergeTypeState(blockStates, falseSucc.order, sFalse || curState)) {
5881
+ if (mergeTypeState(blockStates, falseSucc.order, sFalse || curState, nodeCopyProp)) {
5882
+ if (logThisRun) {
5883
+ console.log(`re-merge: ${top.order} -> ${falseSucc.order}`);
5884
+ }
5661
5885
  queue.enqueue(falseSucc);
5662
5886
  }
5663
5887
  }
5664
5888
  return true;
5665
5889
  }
5890
+ /*
5891
+ * nodeCopyProp contains two related maps. It maps ref nodes
5892
+ * to the def node that should be copy propagated. It also maps
5893
+ * def nodes to false, to indicate a previous failure to find
5894
+ * a copy prop candidate.
5895
+ */
5896
+ const nodeCopyProp = new Map();
5666
5897
  const nodeEquivs = new Map();
5667
5898
  const localDecls = new Map();
5668
5899
  const localConflicts = new Set();
5669
5900
  const selfAssignments = new Set();
5670
5901
  const processEvent = (top, curState, event, skipMerge) => {
5671
5902
  if (!skipMerge && event.mayThrow && top.exsucc) {
5672
- if (mergeTypeState(blockStates, top.exsucc.order, curState)) {
5903
+ if (mergeTypeState(blockStates, top.exsucc.order, curState, nodeCopyProp)) {
5673
5904
  queue.enqueue(top.exsucc);
5674
5905
  }
5675
5906
  }
5907
+ validateTypeState(curState);
5676
5908
  switch (event.type) {
5677
5909
  case "kil": {
5678
5910
  const curEntry = getStateEntry(curState, event.decl);
@@ -5682,11 +5914,15 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5682
5914
  if (curEntry.assocPaths) {
5683
5915
  clearAssocPaths(curState, event.decl, curEntry);
5684
5916
  }
5917
+ if (curEntry.copyPropItem) {
5918
+ copyPropFailed(curState, curEntry.copyPropItem, nodeCopyProp);
5919
+ }
5920
+ clearRelatedCopyPropEvents(curState, event.decl, nodeCopyProp);
5685
5921
  curState.map.delete(event.decl);
5686
5922
  break;
5687
5923
  }
5688
5924
  case "ref": {
5689
- const curEntry = getStateEntry(curState, event.decl);
5925
+ let curEntry = getStateEntry(curState, event.decl);
5690
5926
  typeMap.set(event.node, curEntry.curType);
5691
5927
  nodeEquivs.delete(event.node);
5692
5928
  if (curEntry.equivSet) {
@@ -5698,6 +5934,26 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5698
5934
  });
5699
5935
  }
5700
5936
  }
5937
+ if (copyPropStores) {
5938
+ nodeCopyProp.delete(event.node);
5939
+ if (curEntry.copyPropItem) {
5940
+ const copyPropInfo = copyPropStores.get(curEntry.copyPropItem.event.node);
5941
+ assert(copyPropInfo && copyPropInfo.ref === event.node);
5942
+ const defNode = nodeCopyProp.get(curEntry.copyPropItem.event.node);
5943
+ assert(!defNode || defNode === event.node);
5944
+ if (defNode !== false) {
5945
+ nodeCopyProp.set(event.node, curEntry.copyPropItem.event.node);
5946
+ nodeCopyProp.set(curEntry.copyPropItem.event.node, event.node);
5947
+ }
5948
+ clearCopyProp(curState, curEntry.copyPropItem);
5949
+ curEntry = { ...curEntry };
5950
+ delete curEntry.copyPropItem;
5951
+ curState.map.set(event.decl, curEntry);
5952
+ }
5953
+ else if (declIsNonLocal(event.decl)) {
5954
+ clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
5955
+ }
5956
+ }
5701
5957
  if (logThisRun) {
5702
5958
  console.log(` ${describeEvent(event)} == ${display(curEntry.curType)}`);
5703
5959
  }
@@ -5707,53 +5963,75 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5707
5963
  if (logThisRun) {
5708
5964
  console.log(` ${describeEvent(event)}`);
5709
5965
  }
5710
- let callees = undefined;
5711
- if (event.calleeDecl) {
5712
- const calleeType = getStateType(curState, event.calleeDecl);
5713
- if (hasValue(calleeType) && calleeType.type === 8192 /* TypeTag.Function */) {
5714
- callees = calleeType.value;
5715
- }
5716
- else {
5717
- callees = findCalleesByNode(state, event.node);
5718
- }
5719
- }
5720
- if (callees === undefined && event.callees !== undefined) {
5721
- callees = event.callees;
5722
- }
5723
- if (event.calleeObj) {
5724
- if (handleMod(curState, event.calleeObj, callees, event.node)) {
5725
- break;
5966
+ modInterference(curState, event, true, (callees, calleeObj) => {
5967
+ clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
5968
+ if (calleeObj) {
5969
+ const objType = getStateType(curState, calleeObj);
5970
+ if (objType.type &
5971
+ (32768 /* TypeTag.Object */ | 512 /* TypeTag.Array */ | 1024 /* TypeTag.Dictionary */)) {
5972
+ setStateEvent(curState, calleeObj, objType, 1 /* UpdateKind.Inner */);
5973
+ }
5726
5974
  }
5727
- }
5728
- curState.map.forEach((tsv, decl) => {
5729
- let type = tsv.curType;
5730
- if (!some(decl, (d) => d.type === "VariableDeclarator" &&
5731
- (d.node.kind === "var" ||
5732
- // even a "const" could have its "inner" type altered
5733
- (type.value != null && (type.type & 32768 /* TypeTag.Object */) !== 0)))) {
5734
- return;
5975
+ if (nodeCopyProp.size &&
5976
+ event.node.type === "CallExpression" &&
5977
+ some(callees, (callee) => inlineRequested(state, callee))) {
5978
+ // we don't want to copy-prop to the argument of
5979
+ // an inline function, because that could prevent
5980
+ // inlining.
5981
+ event.node.arguments.forEach((arg) => {
5982
+ const def = nodeCopyProp.get(arg);
5983
+ if (def && nodeCopyProp.get(def) !== false) {
5984
+ nodeCopyProp.set(def, false);
5985
+ nodeCopyProp.delete(arg);
5986
+ }
5987
+ });
5735
5988
  }
5736
- if (modifiableDecl(decl, callees)) {
5737
- if (tsv.equivSet) {
5738
- removeEquiv(curState.map, decl);
5989
+ curState.map.forEach((tsv, decl) => {
5990
+ let type = tsv.curType;
5991
+ if (!some(decl, (d) => d.type === "VariableDeclarator" &&
5992
+ (d.node.kind === "var" ||
5993
+ // even a "const" could have its "inner" type altered
5994
+ (type.value != null && (type.type & 32768 /* TypeTag.Object */) !== 0)))) {
5995
+ return;
5739
5996
  }
5740
- if (tsv.assocPaths) {
5741
- clearAssocPaths(curState, decl, tsv);
5997
+ if (modifiableDecl(decl, callees)) {
5998
+ if (tsv.equivSet) {
5999
+ removeEquiv(curState.map, decl);
6000
+ }
6001
+ if (tsv.assocPaths) {
6002
+ clearAssocPaths(curState, decl, tsv);
6003
+ }
6004
+ // we only attach copyPropItems to locals,
6005
+ // which can't be modified by a call.
6006
+ assert(!tsv.copyPropItem);
6007
+ clearRelatedCopyPropEvents(curState, decl, nodeCopyProp);
6008
+ curState.map.set(decl, { curType: typeConstraint(decl) });
5742
6009
  }
5743
- curState.map.set(decl, { curType: typeConstraint(decl) });
5744
- }
5745
- else if (type.value != null &&
5746
- (!callees || !every(callees, (callee) => callee.info === false))) {
5747
- if (type.type & 32768 /* TypeTag.Object */) {
5748
- const odata = getObjectValue(tsv.curType);
5749
- if (odata?.obj) {
5750
- type = cloneType(type);
5751
- const newData = { klass: odata.klass };
5752
- setUnionComponent(type, 32768 /* TypeTag.Object */, newData);
5753
- curState.map.set(decl, { ...tsv, curType: type });
6010
+ else if (type.value != null &&
6011
+ (!callees || !every(callees, (callee) => callee.info === false))) {
6012
+ if (type.type & 32768 /* TypeTag.Object */) {
6013
+ const odata = getObjectValue(tsv.curType);
6014
+ if (odata?.obj) {
6015
+ type = cloneType(type);
6016
+ const newData = { klass: odata.klass };
6017
+ setUnionComponent(type, 32768 /* TypeTag.Object */, newData);
6018
+ if (tsv.assocPaths) {
6019
+ clearAssocPaths(curState, decl, tsv);
6020
+ tsv = { ...tsv };
6021
+ delete tsv.assocPaths;
6022
+ }
6023
+ if (tsv.copyPropItem) {
6024
+ copyPropFailed(curState, tsv.copyPropItem, nodeCopyProp);
6025
+ tsv = { ...tsv };
6026
+ delete tsv.copyPropItem;
6027
+ }
6028
+ clearRelatedCopyPropEvents(curState, decl, nodeCopyProp);
6029
+ curState.map.set(decl, { ...tsv, curType: type });
6030
+ }
5754
6031
  }
5755
6032
  }
5756
- }
6033
+ });
6034
+ return true;
5757
6035
  });
5758
6036
  break;
5759
6037
  }
@@ -5764,9 +6042,18 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5764
6042
  ? event.node.left
5765
6043
  : null;
5766
6044
  if (lval) {
5767
- const beforeType = getStateType(curState, event.decl);
5768
- if (beforeType) {
5769
- typeMap.set(lval, beforeType);
6045
+ const before = getStateEntry(curState, event.decl);
6046
+ if (before.curType) {
6047
+ typeMap.set(lval, before.curType);
6048
+ }
6049
+ if (before.copyPropItem &&
6050
+ (event.node.type !== "AssignmentExpression" ||
6051
+ event.node.operator !== "=")) {
6052
+ copyPropFailed(curState, before.copyPropItem, nodeCopyProp);
6053
+ const v = { ...before };
6054
+ delete v.copyPropItem;
6055
+ assert(isTypeStateKey(event.decl));
6056
+ curState.map.set(event.decl, v);
5770
6057
  }
5771
6058
  }
5772
6059
  const expr = event.node.type === "VariableDeclarator"
@@ -5775,7 +6062,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5775
6062
  const type = expr
5776
6063
  ? evaluate(istate, expr).value
5777
6064
  : { type: 524287 /* TypeTag.Any */ };
5778
- const wasComputedDecl = setStateEvent(curState, event.decl, type, true);
6065
+ const wasComputedDecl = setStateEvent(curState, event.decl, type, 2 /* UpdateKind.Reassign */);
5779
6066
  some(event.decl, (decl) => {
5780
6067
  if (decl.type !== "VariableDeclarator" ||
5781
6068
  decl.node.kind !== "var" ||
@@ -5806,13 +6093,16 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5806
6093
  if (value.assocPaths) {
5807
6094
  clearAssocPaths(curState, decls, value);
5808
6095
  }
6096
+ assert(!value.copyPropItem);
5809
6097
  curState.map.set(decls, { curType: typeConstraint(decls) });
6098
+ clearRelatedCopyPropEvents(curState, decls, nodeCopyProp);
5810
6099
  }
5811
6100
  });
5812
6101
  }
5813
6102
  if (event.rhs) {
5814
6103
  const selfAssign = addEquiv(curState.map, event.rhs, event.decl);
5815
- if (event.node.type === "AssignmentExpression") {
6104
+ if (event.node.type === "AssignmentExpression" &&
6105
+ event.node.operator === "=") {
5816
6106
  if (selfAssign) {
5817
6107
  // rhs and lhs are identical
5818
6108
  selfAssignments.add(event.node);
@@ -5822,7 +6112,72 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5822
6112
  }
5823
6113
  }
5824
6114
  }
5825
- if (declIsLocal(event.decl)) {
6115
+ if (!declIsLocal(event.decl)) {
6116
+ clearRelatedCopyPropEvents(curState, null, nodeCopyProp);
6117
+ }
6118
+ else {
6119
+ if (event.containedEvents &&
6120
+ copyPropStores &&
6121
+ nodeCopyProp.get(event.node) !== false &&
6122
+ (!event.rhs || !declIsLocal(event.rhs))) {
6123
+ const copyPropCandidate = copyPropStores.get(event.node);
6124
+ if (copyPropCandidate) {
6125
+ const contained = new Map();
6126
+ if (event.containedEvents.every((event) => {
6127
+ if (event.type === "mod") {
6128
+ if (modInterference(curState, event, false, () => false)) {
6129
+ return true;
6130
+ }
6131
+ if (!copyPropCandidate.ant ||
6132
+ // If the ref isn't anticipated, we can't propagate it
6133
+ // in case it has side effects.
6134
+ some(event.calleeDecl, (callee) => callee.type === "FunctionDeclaration" &&
6135
+ inlineRequested(state, callee))) {
6136
+ // Don't copy prop if the rhs is marked for
6137
+ // inline, because we might move it out of
6138
+ // assignment context, to somewhere it can't be
6139
+ // inlined.
6140
+ return false;
6141
+ }
6142
+ }
6143
+ if (!event.decl ||
6144
+ (isTypeStateKey(event.decl) &&
6145
+ some(event.decl, (decl) => (decl.type === "VariableDeclarator" &&
6146
+ decl.node.kind === "var") ||
6147
+ decl.type === "BinaryExpression" ||
6148
+ decl.type === "Identifier"))) {
6149
+ const key = event.decl ?? null;
6150
+ if (key &&
6151
+ declIsLocal(key) &&
6152
+ nodeCopyProp.has(event.node)) {
6153
+ // we might have
6154
+ //
6155
+ // var x = foo();
6156
+ // var y = x + 1;
6157
+ // bar();
6158
+ // return y;
6159
+ //
6160
+ // In that case, its ok to drop "x = foo()" and rewrite as y = foo() + 1"
6161
+ // OR its ok to drop "y = x + 1" and rewrite as "return x + 1".
6162
+ // But we can't do both, and rewrite as "bar(); return foo() + 1;"
6163
+ // So just disable copy prop for *this* node. We'll re-run and have a
6164
+ // second chance later.
6165
+ return false;
6166
+ }
6167
+ const item = contained.get(key);
6168
+ if (!item) {
6169
+ contained.set(key, [event]);
6170
+ }
6171
+ else {
6172
+ item.push(event);
6173
+ }
6174
+ }
6175
+ return true;
6176
+ })) {
6177
+ addCopyPropEvent(curState, { event, contained });
6178
+ }
6179
+ }
6180
+ }
5826
6181
  const name = localDeclName(event.decl);
5827
6182
  const locals = localDecls.get(name);
5828
6183
  if (!locals) {
@@ -5870,7 +6225,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5870
6225
  func.node.params.forEach((param) => {
5871
6226
  setStateEvent(head, param, param.type === "BinaryExpression"
5872
6227
  ? typeFromTypespec(state, param.right)
5873
- : { type: 524287 /* TypeTag.Any */ }, false);
6228
+ : { type: 524287 /* TypeTag.Any */ }, 0 /* UpdateKind.None */);
5874
6229
  });
5875
6230
  queue.enqueue(order[0]);
5876
6231
  while (!queue.empty()) {
@@ -5904,6 +6259,16 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5904
6259
  printBlockTrailer(top);
5905
6260
  }
5906
6261
  }
6262
+ nodeCopyProp.forEach((value, key) => {
6263
+ if (key.type === "VariableDeclarator" ||
6264
+ key.type === "AssignmentExpression") {
6265
+ if (value === false) {
6266
+ nodeCopyProp.delete(key);
6267
+ return;
6268
+ }
6269
+ assert(nodeCopyProp.get(value) === key);
6270
+ }
6271
+ });
5907
6272
  if (logThisRun) {
5908
6273
  order.forEach((block) => {
5909
6274
  printBlockHeader(block);
@@ -5921,9 +6286,29 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5921
6286
  nodeEquivs.forEach((value, key) => {
5922
6287
  console.log(`${formatAst(key)} = [${value.equiv.map((equiv) => tsKey(equiv))}] ${key.loc && key.loc.source ? ` (${sourceLocation(key.loc)})` : ""}`);
5923
6288
  });
6289
+ console.log("====== Copy Prop =====");
6290
+ nodeCopyProp.forEach((value, key) => {
6291
+ assert(value !== false);
6292
+ if (key.type === "VariableDeclarator" ||
6293
+ key.type === "AssignmentExpression") {
6294
+ return;
6295
+ }
6296
+ assert((value.type === "VariableDeclarator" && value.init) ||
6297
+ value.type === "AssignmentExpression");
6298
+ const node = value.type === "VariableDeclarator" ? value.init : value.right;
6299
+ console.log(`${formatAst(key)} = [${formatAstLongLines(node)}] ${key.loc && key.loc.source ? ` (${sourceLocation(key.loc)})` : ""}`);
6300
+ });
6301
+ }
6302
+ if (logThisRun) {
6303
+ console.log(formatAstLongLines(func.node));
6304
+ if (copyPropStores) {
6305
+ copyPropStores.forEach(({ ref, ant }, node) => {
6306
+ console.log(`copy-prop-store: ${formatAstLongLines(node)}${ant ? "!" : ""} => ${nodeCopyProp.get(node) !== ref ? "Failed" : "Success"}`);
6307
+ });
6308
+ }
5924
6309
  }
5925
6310
  if (optimizeEquivalencies) {
5926
- if (!nodeEquivs.size && !selfAssignments.size) {
6311
+ if (!nodeEquivs.size && !selfAssignments.size && !nodeCopyProp.size) {
5927
6312
  return { istate, nodeEquivs };
5928
6313
  }
5929
6314
  if (logThisRun) {
@@ -5967,12 +6352,60 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
5967
6352
  }
5968
6353
  }));
5969
6354
  traverseAst(func.node.body, null, (node) => {
6355
+ const copyNode = nodeCopyProp.get(node);
6356
+ if (copyNode) {
6357
+ if (node.type === "AssignmentExpression") {
6358
+ if (logThisRun) {
6359
+ console.log(`Killing copy-prop assignment ${formatAstLongLines(node)}`);
6360
+ }
6361
+ return withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
6362
+ }
6363
+ if (node.type === "VariableDeclarator") {
6364
+ assert(node.init);
6365
+ if (logThisRun) {
6366
+ console.log(`Killing copy-prop variable initialization ${formatAstLongLines(node)}`);
6367
+ }
6368
+ const dup = { ...node };
6369
+ delete dup.init;
6370
+ return dup;
6371
+ }
6372
+ if (copyNode.type === "AssignmentExpression") {
6373
+ if (logThisRun) {
6374
+ console.log(`copy-prop ${formatAstLongLines(node)} => ${formatAstLongLines(copyNode.right)}`);
6375
+ }
6376
+ return withLocDeep(copyNode.right, node, node, false);
6377
+ }
6378
+ else if (copyNode.type === "VariableDeclarator") {
6379
+ assert(copyNode.init);
6380
+ if (logThisRun) {
6381
+ console.log(`copy-prop ${formatAstLongLines(node)} => ${formatAstLongLines(copyNode.init)}`);
6382
+ }
6383
+ return withLocDeep(copyNode.init, node, node, false);
6384
+ }
6385
+ assert(false);
6386
+ }
5970
6387
  if (selfAssignments.has(node)) {
5971
6388
  if (logThisRun) {
5972
6389
  console.log(`Deleting self assignment: ${formatAst(node)} (${sourceLocation(node.loc)})`);
5973
6390
  }
5974
6391
  return withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
5975
6392
  }
6393
+ if (nodeCopyProp.size) {
6394
+ /*
6395
+ * Copy prop and equiv can interfere with each other:
6396
+ *
6397
+ * var c = g; // copy prop kills this
6398
+ * ...
6399
+ * var x = g + 2; // node equiv replaces g with c
6400
+ * ...
6401
+ * return c; // copy prop changes this to g
6402
+ *
6403
+ * So ignore equivalencies if copy prop is active.
6404
+ * Note that we have to re-run propagation anyway
6405
+ * if copy prop did anything.
6406
+ */
6407
+ return null;
6408
+ }
5976
6409
  const equiv = nodeEquivs.get(node);
5977
6410
  if (!equiv || localConflicts.has(equiv.decl))
5978
6411
  return null;
@@ -6024,7 +6457,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
6024
6457
  return replacement;
6025
6458
  });
6026
6459
  }
6027
- return { istate, nodeEquivs };
6460
+ return {
6461
+ istate,
6462
+ nodeEquivs,
6463
+ redo: optimizeEquivalencies && nodeCopyProp.size,
6464
+ };
6028
6465
  }
6029
6466
  function updateByAssocPath(path, property, union) {
6030
6467
  const valueToStore = (base) => {
@@ -6261,20 +6698,48 @@ function couldBeObj(a, b) {
6261
6698
 
6262
6699
  "use strict";
6263
6700
  /* unused harmony exports findDeadStores, eliminateDeadStores */
6264
- /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
6265
- /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
6266
- /* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
6267
- /* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5101);
6268
- /* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8180);
6269
- /* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(333);
6270
- /* harmony import */ var _type_flow_util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1638);
6701
+ /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4143);
6702
+ /* harmony import */ var node_assert__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_assert__WEBPACK_IMPORTED_MODULE_0__);
6703
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6817);
6704
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_1__);
6705
+ /* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6652);
6706
+ /* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5101);
6707
+ /* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8180);
6708
+ /* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(333);
6709
+ /* harmony import */ var _type_flow_util__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1638);
6710
+
6271
6711
 
6272
6712
 
6273
6713
 
6274
6714
 
6275
6715
 
6276
6716
 
6277
- function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6717
+ function cloneAnt(antMap) {
6718
+ const ant = new Map();
6719
+ antMap.forEach((s, k) => ant.set(k, new Set(s)));
6720
+ return ant;
6721
+ }
6722
+ function addAnt(antMap, decl, node) {
6723
+ assert(node.type === "Identifier");
6724
+ const ant = antMap.get(decl);
6725
+ if (!ant) {
6726
+ antMap.set(decl, new Set([node]));
6727
+ }
6728
+ else {
6729
+ ant.add(node);
6730
+ }
6731
+ }
6732
+ function cloneState(blockState) {
6733
+ const clone = { dead: new Set(blockState.dead) };
6734
+ if (blockState.anticipated) {
6735
+ clone.anticipated = cloneAnt(blockState.anticipated);
6736
+ }
6737
+ if (blockState.partiallyAnticipated) {
6738
+ clone.partiallyAnticipated = cloneAnt(blockState.partiallyAnticipated);
6739
+ }
6740
+ return clone;
6741
+ }
6742
+ function findDeadStores(func, graph, nodeEquivs, findCopyPropCandidates, logThisRun) {
6278
6743
  const order = getPostOrder(graph);
6279
6744
  order.forEach((block, i) => {
6280
6745
  block.order = i;
@@ -6282,13 +6747,62 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6282
6747
  const blockStates = [];
6283
6748
  const nodeConflicts = nodeEquivs && new Map();
6284
6749
  const mergeStates = (to, from) => {
6285
- return Array.from(to).reduce((changed, decl) => {
6286
- if (!from.has(decl)) {
6287
- to.delete(decl);
6750
+ let changed = Array.from(to.dead).reduce((changed, decl) => {
6751
+ if (!from.dead.has(decl)) {
6752
+ to.dead.delete(decl);
6288
6753
  changed = true;
6289
6754
  }
6290
6755
  return changed;
6291
6756
  }, false);
6757
+ if (to.anticipated) {
6758
+ if (!from.anticipated) {
6759
+ delete to.anticipated;
6760
+ changed = true;
6761
+ }
6762
+ else {
6763
+ changed = Array.from(to.anticipated).reduce((changed, [decl, toant]) => {
6764
+ const fromant = from.anticipated.get(decl);
6765
+ if (!fromant) {
6766
+ to.anticipated.delete(decl);
6767
+ changed = true;
6768
+ }
6769
+ else {
6770
+ toant.forEach((node) => {
6771
+ if (!fromant.has(node)) {
6772
+ toant.delete(node);
6773
+ changed = true;
6774
+ }
6775
+ });
6776
+ }
6777
+ return changed;
6778
+ }, changed);
6779
+ }
6780
+ }
6781
+ if (from.partiallyAnticipated) {
6782
+ if (!to.partiallyAnticipated) {
6783
+ to.partiallyAnticipated = cloneAnt(from.partiallyAnticipated);
6784
+ changed = true;
6785
+ }
6786
+ else {
6787
+ changed = Array.from(from.partiallyAnticipated).reduce((changed, [decl, fromant]) => {
6788
+ const toant = to.partiallyAnticipated.get(decl);
6789
+ if (!toant) {
6790
+ to.partiallyAnticipated.set(decl, fromant);
6791
+ changed = true;
6792
+ }
6793
+ else {
6794
+ fromant.forEach((node) => {
6795
+ if (!toant.has(node)) {
6796
+ changed = true;
6797
+ toant.add(node);
6798
+ }
6799
+ });
6800
+ }
6801
+ return changed;
6802
+ }, changed);
6803
+ }
6804
+ }
6805
+ return changed;
6292
6806
  };
6293
6807
  const queue = new DataflowQueue();
6294
6808
  const locals = new Set(order
@@ -6299,10 +6813,11 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6299
6813
  : [])
6300
6814
  .concat(func.node.params));
6301
6815
  const deadStores = new Set();
6816
+ const copyPropStores = new Map();
6302
6817
  order.forEach((block) => {
6303
6818
  if (!block.succs) {
6304
6819
  queue.enqueue(block);
6305
- blockStates[block.order] = new Set(locals);
6820
+ blockStates[block.order] = { dead: new Set(locals) };
6306
6821
  }
6307
6822
  });
6308
6823
  while (!queue.empty()) {
@@ -6313,10 +6828,10 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6313
6828
  if (!blockStates[top.order]) {
6314
6829
  throw new Error(`Block ${top.order || 0} had no state!`);
6315
6830
  }
6316
- const curState = new Set(blockStates[top.order]);
6831
+ const curState = cloneState(blockStates[top.order]);
6317
6832
  if (logThisRun) {
6318
6833
  printBlockHeader(top);
6319
- curState.forEach((decl) => console.log(` - anticipated: ${tsKey(decl)}`));
6834
+ curState.dead.forEach((decl) => console.log(` - anticipated: ${tsKey(decl)}`));
6320
6835
  }
6321
6836
  if (top.events) {
6322
6837
  for (let i = top.events.length; i--;) {
@@ -6331,33 +6846,67 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6331
6846
  switch (event.type) {
6332
6847
  case "ref":
6333
6848
  if (isTypeStateKey(event.decl)) {
6334
- curState.delete(event.decl);
6335
6849
  if (logThisRun) {
6850
+ console.log(describeEvent(event));
6336
6851
  console.log(` kill => ${tsKey(event.decl)}`);
6337
6852
  }
6853
+ if (findCopyPropCandidates && declIsLocal(event.decl)) {
6854
+ if (!curState.anticipated) {
6855
+ curState.anticipated = new Map();
6856
+ }
6857
+ addAnt(curState.anticipated, event.decl, event.node);
6858
+ if (!curState.partiallyAnticipated) {
6859
+ curState.partiallyAnticipated = new Map();
6860
+ }
6861
+ addAnt(curState.partiallyAnticipated, event.decl, event.node);
6862
+ if (logThisRun) {
6863
+ console.log(` antrefs: ${curState.partiallyAnticipated.get(event.decl)?.size ?? 0} ${curState.anticipated.get(event.decl)?.size ?? 0}`);
6864
+ }
6865
+ }
6866
+ curState.dead.delete(event.decl);
6338
6867
  }
6339
6868
  break;
6340
6869
  case "def":
6341
6870
  if (isTypeStateKey(event.decl) &&
6342
6871
  (event.node.type !== "VariableDeclarator" || event.node.init)) {
6343
- if (curState.has(event.decl)) {
6872
+ if (logThisRun) {
6873
+ console.log(describeEvent(event));
6874
+ }
6875
+ const assignNode = (event.node.type === "AssignmentExpression" &&
6876
+ event.node.operator === "=" &&
6877
+ event.node.right) ||
6878
+ (event.node.type === "VariableDeclarator" && event.node.init);
6879
+ if (curState.dead.has(event.decl)) {
6344
6880
  deadStores.add(event.node);
6345
6881
  }
6346
6882
  else {
6347
6883
  deadStores.delete(event.node);
6884
+ if (assignNode &&
6885
+ declIsLocal(event.decl) &&
6886
+ curState.partiallyAnticipated) {
6887
+ const pant = curState.partiallyAnticipated.get(event.decl);
6888
+ if (pant && pant.size === 1) {
6889
+ if (logThisRun) {
6890
+ console.log(` is copy-prop-candidate ${curState.anticipated?.get(event.decl)?.size ?? 0}`);
6891
+ }
6892
+ copyPropStores.set(event.node, {
6893
+ ref: Array.from(pant)[0],
6894
+ ant: curState.anticipated?.get(event.decl)?.size === 1,
6895
+ });
6896
+ }
6897
+ curState.partiallyAnticipated.delete(event.decl);
6898
+ curState.anticipated?.delete(event.decl);
6899
+ }
6900
+ else {
6901
+ copyPropStores.delete(event.node);
6902
+ }
6348
6903
  }
6349
6904
  if (nodeConflicts) {
6350
6905
  const conflicts = new Set(locals);
6351
- curState.forEach((dead) => conflicts.delete(dead));
6906
+ curState.dead.forEach((dead) => conflicts.delete(dead));
6352
6907
  if (event.rhs) {
6353
6908
  conflicts.delete(event.rhs);
6354
- const equiv = event.node.type === "AssignmentExpression" &&
6355
- event.node.operator === "="
6356
- ? nodeEquivs.get(event.node.right)
6357
- : event.node.type === "VariableDeclarator" &&
6358
- event.node.init
6359
- ? nodeEquivs.get(event.node.init)
6360
- : null;
6909
+ const equiv = assignNode && nodeEquivs.get(assignNode);
6361
6910
  if (equiv) {
6362
6911
  equiv.equiv.forEach((e) => isTypeStateKey(e) && conflicts.delete(e));
6363
6912
  isTypeStateKey(equiv.decl) && conflicts.delete(equiv.decl);
@@ -6366,10 +6915,8 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6366
6915
  conflicts.add(event.decl);
6367
6916
  nodeConflicts.set(event.node, conflicts);
6368
6917
  }
6369
- if ((event.node.type === "AssignmentExpression" &&
6370
- event.node.operator === "=") ||
6371
- (event.node.type === "VariableDeclarator" && event.node.init)) {
6372
- curState.add(event.decl);
6918
+ if (assignNode) {
6919
+ curState.dead.add(event.decl);
6373
6920
  if (logThisRun) {
6374
6921
  console.log(` anticipated => ${tsKey(event.decl)}`);
6375
6922
  }
@@ -6378,14 +6925,14 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6378
6925
  break;
6379
6926
  case "kil":
6380
6927
  if (isTypeStateKey(event.decl)) {
6381
- curState.add(event.decl);
6928
+ curState.dead.add(event.decl);
6382
6929
  if (logThisRun) {
6383
6930
  console.log(` anticipated => ${tsKey(event.decl)}`);
6384
6931
  }
6385
6932
  }
6386
6933
  break;
6387
6934
  case "mod":
6388
- curState.forEach((decl) => declIsLocal(decl) || curState.delete(decl));
6935
+ curState.dead.forEach((decl) => declIsLocal(decl) || curState.dead.delete(decl));
6389
6936
  break;
6390
6937
  case "flw":
6391
6938
  break;
@@ -6395,7 +6942,7 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6395
6942
  const doMerge = (pred) => {
6396
6943
  const pi = pred.order || 0;
6397
6944
  if (!blockStates[pi]) {
6398
- blockStates[pi] = new Set(curState);
6945
+ blockStates[pi] = cloneState(curState);
6399
6946
  queue.enqueue(pred);
6400
6947
  }
6401
6948
  else if (mergeStates(blockStates[pi], curState)) {
@@ -6427,15 +6974,18 @@ function findDeadStores(func, graph, nodeEquivs, logThisRun) {
6427
6974
  nodeConflicts.forEach((conflicts) => conflicts.forEach((conflict) => addConflicts(conflict, conflicts)));
6428
6975
  func.node.params.forEach((param, index, arr) => addConflicts(param, arr));
6429
6976
  }
6430
- return { deadStores, locals, localConflicts };
6977
+ return { deadStores, locals, localConflicts, copyPropStores };
6431
6978
  }
6432
6979
  function eliminateDeadStores(state, func, graph, logThisRun) {
6433
- const { deadStores } = findDeadStores(func, graph, null, logThisRun);
6980
+ const { deadStores, copyPropStores } = findDeadStores(func, graph, null, state.config?.singleUseCopyProp ?? true, logThisRun);
6434
6981
  if (!deadStores.size)
6435
- return false;
6982
+ return { changes: false, copyPropStores };
6436
6983
  if (logThisRun) {
6437
6984
  console.log("====== Dead Stores =====");
6438
- deadStores.forEach((dead) => console.log(`${formatAst(dead)} (${sourceLocation(dead.loc)})`));
6985
+ deadStores.forEach((dead) => (dead.type === "AssignmentExpression" ||
6986
+ dead.type === "UpdateExpression" ||
6987
+ dead.type === "VariableDeclarator") &&
6988
+ console.log(`${formatAst(dead)} (${sourceLocation(dead.loc)})`));
6439
6989
  }
6440
6990
  let changes = false;
6441
6991
  traverseAst(func.node.body, null, (node, parent) => {
@@ -6498,7 +7048,7 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
6498
7048
  }
6499
7049
  return null;
6500
7050
  });
6501
- return changes;
7051
+ return { copyPropStores, changes };
6502
7052
  }
6503
7053
 
6504
7054
 
@@ -6509,9 +7059,13 @@ function eliminateDeadStores(state, func, graph, logThisRun) {
6509
7059
 
6510
7060
  "use strict";
6511
7061
  /* unused harmony exports evaluateBinaryTypes, evaluateLogicalTypes */
6512
- /* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7161);
6513
- /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7255);
6514
- /* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(757);
7062
+ /* harmony import */ var _could_be__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4055);
7063
+ /* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7161);
7064
+ /* harmony import */ var _sub_type__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9234);
7065
+ /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7255);
7066
+ /* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(757);
7067
+
7068
+
6515
7069
 
6516
7070
 
6517
7071
 
@@ -6842,14 +7396,6 @@ function evaluateBinaryTypes(op, left, right) {
6842
7396
  : 2 /* TypeTag.False */,
6843
7397
  }),
6844
7398
  },
6845
- instanceof: {
6846
- allowed: 524287 /* TypeTag.Any */,
6847
- typeFn: () => ({
6848
- tag: 6 /* TypeTag.Boolean */,
6849
- castArgs: false,
6850
- }),
6851
- valueFn: (_left, _right) => undefined,
6852
- },
6853
7399
  has: {
6854
7400
  allowed: 524287 /* TypeTag.Any */,
6855
7401
  typeFn: () => ({
@@ -6860,6 +7406,21 @@ function evaluateBinaryTypes(op, left, right) {
6860
7406
  },
6861
7407
  };
6862
7408
  }
7409
+ if (op === "instanceof") {
7410
+ if (right.type & 16384 /* TypeTag.Class */) {
7411
+ if (!isExact(right)) {
7412
+ return { type: 6 /* TypeTag.Boolean */ };
7413
+ }
7414
+ right = { type: 32768 /* TypeTag.Object */, value: { klass: right } };
7415
+ }
7416
+ return {
7417
+ type: subtypeOf(left, right)
7418
+ ? 4 /* TypeTag.True */
7419
+ : !couldBeWeak(left, right)
7420
+ ? 2 /* TypeTag.False */
7421
+ : 6 /* TypeTag.Boolean */,
7422
+ };
7423
+ }
6863
7424
  const info = operators[op];
6864
7425
  if (!info)
6865
7426
  return { type: 524287 /* TypeTag.Any */ };
@@ -6982,7 +7543,7 @@ function checkCallArgs(istate, node, callees, args) {
6982
7543
  let argEffects = true;
6983
7544
  const object = calleeObjectType(istate, node.callee);
6984
7545
  if (object) {
6985
- const info = sysCallInfo(cur);
7546
+ const info = sysCallInfo(istate.state, cur);
6986
7547
  if (info) {
6987
7548
  const result = info(istate.state, cur, object, () => args);
6988
7549
  if (result.argTypes)
@@ -7113,16 +7674,18 @@ function isOverride(cur, funcs) {
7113
7674
  return false;
7114
7675
  }
7115
7676
  let systemCallInfo = null;
7116
- function sysCallInfo(func) {
7117
- const info = getSystemCallTable();
7677
+ let systemCallVersion;
7678
+ function sysCallInfo(state, func) {
7679
+ const info = getSystemCallTable(state);
7118
7680
  if (hasProperty(info, func.fullName)) {
7119
7681
  return info[func.fullName];
7120
7682
  }
7121
7683
  return null;
7122
7684
  }
7123
- function getSystemCallTable() {
7124
- if (systemCallInfo)
7685
+ function getSystemCallTable(state) {
7686
+ if (systemCallInfo && systemCallVersion === state.sdk) {
7125
7687
  return systemCallInfo;
7688
+ }
7126
7689
  const arrayAdd = (state, callee, calleeObj, getArgs) => {
7127
7690
  const ret = {};
7128
7691
  if (calleeObj.type & 512 /* TypeTag.Array */) {
@@ -7342,7 +7905,8 @@ function getSystemCallTable() {
7342
7905
  }
7343
7906
  return results;
7344
7907
  };
7345
- return (systemCallInfo = {
7908
+ systemCallVersion = state.sdk;
7909
+ return (systemCallInfo = expandKeys(state, {
7346
7910
  "$.Toybox.Lang.Array.add": arrayAdd,
7347
7911
  "$.Toybox.Lang.Array.addAll": arrayAdd,
7348
7912
  "$.Toybox.Lang.Array.remove": mod,
@@ -7385,7 +7949,47 @@ function getSystemCallTable() {
7385
7949
  "$.Toybox.Math.variance": nop,
7386
7950
  "$.Toybox.Math.srand": mod,
7387
7951
  "$.Toybox.Math.rand": mod,
7952
+ "$.Toybox.Lang.*.(to*|equals|abs)": nop,
7953
+ "$.Toybox.Time.Gregorian.(duration|info|localMoment|moment|utcInfo)": nop,
7954
+ "$.Toybox.Time.(Duration|LocalMoment|Moment).(?!initialize)*": nop,
7955
+ "$.Toybox.Graphics.Dc.get*": nop,
7956
+ }));
7957
+ }
7958
+ function expandKeys(state, table) {
7959
+ const result = {};
7960
+ const pattern = /[*()|]/;
7961
+ Object.entries(table).forEach(([key, value]) => {
7962
+ if (!pattern.test(key)) {
7963
+ result[key] = value;
7964
+ return;
7965
+ }
7966
+ if (state.stack) {
7967
+ const decls = key
7968
+ .split(".")
7969
+ .slice(1)
7970
+ .reduce((decls, decl) => {
7971
+ if (pattern.test(decl)) {
7972
+ const re = new RegExp(`^${decl.replace(/\*/g, ".*")}$`);
7973
+ return decls.flatMap((sn) => isStateNode(sn) && sn.decls
7974
+ ? Object.keys(sn.decls)
7975
+ .filter((m) => re.test(m))
7976
+ .flatMap((m) => sn.decls[m])
7977
+ : []);
7978
+ }
7979
+ else {
7980
+ return decls.flatMap((sn) => (isStateNode(sn) && sn.decls?.[decl]) || []);
7981
+ }
7982
+ }, [state.stack[0].sn]);
7983
+ decls.forEach((decl) => {
7984
+ if (decl.type === "FunctionDeclaration") {
7985
+ if (!hasProperty(result, decl.fullName)) {
7986
+ result[decl.fullName] = value;
7987
+ }
7988
+ }
7989
+ });
7990
+ }
7388
7991
  });
7992
+ return result;
7389
7993
  }
7390
7994
 
7391
7995
 
@@ -9064,6 +9668,24 @@ function optimizeFunction(state, func) {
9064
9668
  }
9065
9669
  function beforeEvaluate(istate, node) {
9066
9670
  switch (node.type) {
9671
+ case "ExpressionStatement": {
9672
+ if (node.expression.type !== "Literal") {
9673
+ const expression = popIstate(istate, node.expression);
9674
+ if (expression.embeddedEffects) {
9675
+ istate.stack.push(expression);
9676
+ }
9677
+ else {
9678
+ const rep = withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
9679
+ istate.stack.push({
9680
+ value: { type: 1 /* TypeTag.Null */ },
9681
+ embeddedEffects: false,
9682
+ node: rep,
9683
+ });
9684
+ node.expression = rep;
9685
+ }
9686
+ }
9687
+ break;
9688
+ }
9067
9689
  case "ConditionalExpression": {
9068
9690
  let alternate = tryPop(istate, node.alternate);
9069
9691
  let consequent = tryPop(istate, node.consequent);
@@ -9812,7 +10434,7 @@ function subtypeOfObj(a, b) {
9812
10434
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9813
10435
  /* harmony export */ "nK": () => (/* binding */ findObjectDeclsByProperty)
9814
10436
  /* harmony export */ });
9815
- /* unused harmony exports isTypeStateKey, declIsLocal, localDeclName, tsKey, sourceLocation, printBlockHeader, describeEvent, printBlockEvents, printBlockTrailer, refineObjectTypeByDecls, findNextObjectType, resolveDottedMember */
10437
+ /* unused harmony exports isTypeStateKey, declIsLocal, declIsNonLocal, localDeclName, tsKey, sourceLocation, printBlockHeader, describeEvent, printBlockEvents, printBlockTrailer, refineObjectTypeByDecls, findNextObjectType, resolveDottedMember */
9816
10438
  /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
9817
10439
  /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
9818
10440
  /* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8180);
@@ -9838,6 +10460,9 @@ function declIsLocal(decl) {
9838
10460
  d.type === "Identifier" ||
9839
10461
  (d.type === "VariableDeclarator" && isLocal(d)));
9840
10462
  }
10463
+ function declIsNonLocal(decl) {
10464
+ return some(decl, (d) => d.type === "VariableDeclarator" && !isLocal(d));
10465
+ }
9841
10466
  function localDeclName(decl) {
9842
10467
  if (Array.isArray(decl))
9843
10468
  decl = decl[0];
@@ -11300,6 +11925,23 @@ function cleanupUnusedVars(state, node) {
11300
11925
  return false;
11301
11926
  const varDeclarations = new Map();
11302
11927
  const stack = [];
11928
+ /*
11929
+ * Every local mentioned in toRemove can be removed, but
11930
+ * occurances of the identifier prior to its declaration
11931
+ * must be non-local. So reconstruct the toRemove record
11932
+ * as we go. This is to prevent issues with something like
11933
+ *
11934
+ * var g = 0;
11935
+ * function foo(var h) {
11936
+ * // if we just consult toRemove, we'll remove this assignment
11937
+ * g = h * 2;
11938
+ * var x = g;
11939
+ * ...
11940
+ * // *This* g is unused, and "g" gets added to activeRemove here.
11941
+ * var g = 42;
11942
+ * }
11943
+ */
11944
+ const activeRemove = {};
11303
11945
  let changes = false;
11304
11946
  traverseAst(node, (node) => {
11305
11947
  switch (node.type) {
@@ -11320,6 +11962,7 @@ function cleanupUnusedVars(state, node) {
11320
11962
  node.declarations.forEach((decl, i) => {
11321
11963
  const name = variableDeclarationName(decl.id);
11322
11964
  if (hasProperty(toRemove, name)) {
11965
+ activeRemove[name] = toRemove[name];
11323
11966
  const info = varDeclarations.get(node);
11324
11967
  if (info) {
11325
11968
  info.indices.push(i);
@@ -11337,14 +11980,14 @@ function cleanupUnusedVars(state, node) {
11337
11980
  case "ExpressionStatement":
11338
11981
  if (node.expression.type === "AssignmentExpression") {
11339
11982
  if (node.expression.left.type === "Identifier" &&
11340
- hasProperty(toRemove, node.expression.left.name)) {
11983
+ hasProperty(activeRemove, node.expression.left.name)) {
11341
11984
  changes = true;
11342
11985
  return unused(state, node.expression.right);
11343
11986
  }
11344
11987
  }
11345
11988
  else if (node.expression.type === "UpdateExpression" &&
11346
11989
  node.expression.argument.type === "Identifier" &&
11347
- hasProperty(toRemove, node.expression.argument.name)) {
11990
+ hasProperty(activeRemove, node.expression.argument.name)) {
11348
11991
  return false;
11349
11992
  }
11350
11993
  break;
@@ -11353,7 +11996,7 @@ function cleanupUnusedVars(state, node) {
11353
11996
  const expr = node.expressions[i];
11354
11997
  if (expr.type === "AssignmentExpression") {
11355
11998
  if (expr.left.type === "Identifier" &&
11356
- hasProperty(toRemove, expr.left.name)) {
11999
+ hasProperty(activeRemove, expr.left.name)) {
11357
12000
  const rep = unused(state, expr.right);
11358
12001
  if (!rep.length) {
11359
12002
  changes = true;
@@ -11362,14 +12005,14 @@ function cleanupUnusedVars(state, node) {
11362
12005
  else {
11363
12006
  // Sequence expressions can only be assignments
11364
12007
  // or update expressions. Even calls aren't allowed
11365
- toRemove[expr.left.name] = null;
12008
+ activeRemove[expr.left.name] = null;
11366
12009
  expr.operator = "=";
11367
12010
  }
11368
12011
  }
11369
12012
  }
11370
12013
  else if (expr.type === "UpdateExpression" &&
11371
12014
  expr.argument.type === "Identifier" &&
11372
- hasProperty(toRemove, expr.argument.name)) {
12015
+ hasProperty(activeRemove, expr.argument.name)) {
11373
12016
  changes = true;
11374
12017
  node.expressions.splice(i, 1);
11375
12018
  }
@@ -11385,7 +12028,7 @@ function cleanupUnusedVars(state, node) {
11385
12028
  const i = info.indices[ii];
11386
12029
  const vdecl = decl.declarations[i];
11387
12030
  const name = variableDeclarationName(vdecl.id);
11388
- if (hasProperty(toRemove, name)) {
12031
+ if (hasProperty(activeRemove, name)) {
11389
12032
  const rep = vdecl.init ? unused(state, vdecl.init) : [];
11390
12033
  if (rep.length) {
11391
12034
  if ((state.sdkVersion || 0) < 4001007 &&
@@ -11430,7 +12073,7 @@ function cleanupUnusedVars(state, node) {
11430
12073
  j = i;
11431
12074
  continue;
11432
12075
  }
11433
- if (toRemove[name]) {
12076
+ if (activeRemove[name]) {
11434
12077
  changes = true;
11435
12078
  j--;
11436
12079
  decl.declarations.splice(i, 1);
@@ -13392,13 +14035,14 @@ async function createDocumentationMap(functionDocumentation) {
13392
14035
  state.allFunctions[info.name]?.forEach((decl) => decl.node?.loc?.source === "api.mir" &&
13393
14036
  decl.fullName.endsWith(`.${info.parent}.${info.name}`) &&
13394
14037
  docMap.set(decl.fullName, info.doc
14038
+ .replace(/@example\s*(.*?)<br\/>(.*?)(@|$)/g, (match, title, m1, m2) => `\n#### Example: ${title}\n\`\`\`${m1.replace(/<br\/>/g, "\n")}\`\`\`${m2}`)
13395
14039
  .replace(/(\*.*?)\s*<br\/>\s*(?!\s*\*)/g, "$1\n\n")
13396
- .replace(/@example(.*?)(@|$)/g, (match, m1, m2) => `\n#### Example\n\`\`\`${m1.replace(/<br\/>/g, "\n")}\`\`\`${m2}`)
13397
14040
  .replace(/@note/g, "\n#### Note\n")
13398
14041
  .replace(/@see/, "\n#### See Also:\n$&")
13399
14042
  .replace(/@see\s+(.*?)(?=<br\/>)/g, "\n * {$1}")
13400
14043
  .replace(/@throws/, "\n#### Throws:\n$&")
13401
14044
  .replace(/@throws\s+(.*?)(?=<br\/>)/g, "\n * $1")
14045
+ .replace(/@option\s+\w+\s+(.*?)(?=<br\/>)/g, "\n - $1")
13402
14046
  .replace(/@since\s+(.*?)(?=<br\/>)/, "\n#### Since:\nAPI Level $1\n")
13403
14047
  .replace(/<div class="description">/, "### Description\n")
13404
14048
  .replace(/<div class="param">/, "\n#### Parameters\n$&")