@markw65/monkeyc-optimizer 1.0.28 → 1.0.29

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.
@@ -2172,6 +2172,185 @@ function pendGo(self, fn) {
2172
2172
  }
2173
2173
 
2174
2174
 
2175
+ /***/ }),
2176
+
2177
+ /***/ 2789:
2178
+ /***/ ((module) => {
2179
+
2180
+ /**
2181
+ * Expose `PriorityQueue`.
2182
+ */
2183
+ module.exports = PriorityQueue;
2184
+
2185
+ /**
2186
+ * Initializes a new empty `PriorityQueue` with the given `comparator(a, b)`
2187
+ * function, uses `.DEFAULT_COMPARATOR()` when no function is provided.
2188
+ *
2189
+ * The comparator function must return a positive number when `a > b`, 0 when
2190
+ * `a == b` and a negative number when `a < b`.
2191
+ *
2192
+ * @param {Function}
2193
+ * @return {PriorityQueue}
2194
+ * @api public
2195
+ */
2196
+ function PriorityQueue(comparator) {
2197
+ this._comparator = comparator || PriorityQueue.DEFAULT_COMPARATOR;
2198
+ this._elements = [];
2199
+ }
2200
+
2201
+ /**
2202
+ * Compares `a` and `b`, when `a > b` it returns a positive number, when
2203
+ * it returns 0 and when `a < b` it returns a negative number.
2204
+ *
2205
+ * @param {String|Number} a
2206
+ * @param {String|Number} b
2207
+ * @return {Number}
2208
+ * @api public
2209
+ */
2210
+ PriorityQueue.DEFAULT_COMPARATOR = function(a, b) {
2211
+ if (typeof a === 'number' && typeof b === 'number') {
2212
+ return a - b;
2213
+ } else {
2214
+ a = a.toString();
2215
+ b = b.toString();
2216
+
2217
+ if (a == b) return 0;
2218
+
2219
+ return (a > b) ? 1 : -1;
2220
+ }
2221
+ };
2222
+
2223
+ /**
2224
+ * Returns whether the priority queue is empty or not.
2225
+ *
2226
+ * @return {Boolean}
2227
+ * @api public
2228
+ */
2229
+ PriorityQueue.prototype.isEmpty = function() {
2230
+ return this.size() === 0;
2231
+ };
2232
+
2233
+ /**
2234
+ * Peeks at the top element of the priority queue.
2235
+ *
2236
+ * @return {Object}
2237
+ * @throws {Error} when the queue is empty.
2238
+ * @api public
2239
+ */
2240
+ PriorityQueue.prototype.peek = function() {
2241
+ if (this.isEmpty()) throw new Error('PriorityQueue is empty');
2242
+
2243
+ return this._elements[0];
2244
+ };
2245
+
2246
+ /**
2247
+ * Dequeues the top element of the priority queue.
2248
+ *
2249
+ * @return {Object}
2250
+ * @throws {Error} when the queue is empty.
2251
+ * @api public
2252
+ */
2253
+ PriorityQueue.prototype.deq = function() {
2254
+ var first = this.peek();
2255
+ var last = this._elements.pop();
2256
+ var size = this.size();
2257
+
2258
+ if (size === 0) return first;
2259
+
2260
+ this._elements[0] = last;
2261
+ var current = 0;
2262
+
2263
+ while (current < size) {
2264
+ var largest = current;
2265
+ var left = (2 * current) + 1;
2266
+ var right = (2 * current) + 2;
2267
+
2268
+ if (left < size && this._compare(left, largest) >= 0) {
2269
+ largest = left;
2270
+ }
2271
+
2272
+ if (right < size && this._compare(right, largest) >= 0) {
2273
+ largest = right;
2274
+ }
2275
+
2276
+ if (largest === current) break;
2277
+
2278
+ this._swap(largest, current);
2279
+ current = largest;
2280
+ }
2281
+
2282
+ return first;
2283
+ };
2284
+
2285
+ /**
2286
+ * Enqueues the `element` at the priority queue and returns its new size.
2287
+ *
2288
+ * @param {Object} element
2289
+ * @return {Number}
2290
+ * @api public
2291
+ */
2292
+ PriorityQueue.prototype.enq = function(element) {
2293
+ var size = this._elements.push(element);
2294
+ var current = size - 1;
2295
+
2296
+ while (current > 0) {
2297
+ var parent = Math.floor((current - 1) / 2);
2298
+
2299
+ if (this._compare(current, parent) <= 0) break;
2300
+
2301
+ this._swap(parent, current);
2302
+ current = parent;
2303
+ }
2304
+
2305
+ return size;
2306
+ };
2307
+
2308
+ /**
2309
+ * Returns the size of the priority queue.
2310
+ *
2311
+ * @return {Number}
2312
+ * @api public
2313
+ */
2314
+ PriorityQueue.prototype.size = function() {
2315
+ return this._elements.length;
2316
+ };
2317
+
2318
+ /**
2319
+ * Iterates over queue elements
2320
+ *
2321
+ * @param {Function} fn
2322
+ */
2323
+ PriorityQueue.prototype.forEach = function(fn) {
2324
+ return this._elements.forEach(fn);
2325
+ };
2326
+
2327
+ /**
2328
+ * Compares the values at position `a` and `b` in the priority queue using its
2329
+ * comparator function.
2330
+ *
2331
+ * @param {Number} a
2332
+ * @param {Number} b
2333
+ * @return {Number}
2334
+ * @api private
2335
+ */
2336
+ PriorityQueue.prototype._compare = function(a, b) {
2337
+ return this._comparator(this._elements[a], this._elements[b]);
2338
+ };
2339
+
2340
+ /**
2341
+ * Swaps the values at position `a` and `b` in the priority queue.
2342
+ *
2343
+ * @param {Number} a
2344
+ * @param {Number} b
2345
+ * @api private
2346
+ */
2347
+ PriorityQueue.prototype._swap = function(a, b) {
2348
+ var aux = this._elements[a];
2349
+ this._elements[a] = this._elements[b];
2350
+ this._elements[b] = aux;
2351
+ };
2352
+
2353
+
2175
2354
  /***/ }),
2176
2355
 
2177
2356
  /***/ 4286:
@@ -9874,17 +10053,17 @@ async function build_project(product, options, lineCallback) {
9874
10053
  if (!product) {
9875
10054
  extraArgs.push("-e");
9876
10055
  }
9877
- switch (typeCheckLevel) {
9878
- case "Off":
10056
+ switch (typeCheckLevel?.toLowerCase()) {
10057
+ case "off":
9879
10058
  extraArgs.push("-l", "0");
9880
10059
  break;
9881
- case "Gradual":
10060
+ case "gradual":
9882
10061
  extraArgs.push("-l", "1");
9883
10062
  break;
9884
- case "Informative":
10063
+ case "informative":
9885
10064
  extraArgs.push("-l", "2");
9886
10065
  break;
9887
- case "Strict":
10066
+ case "strict":
9888
10067
  extraArgs.push("-l", "3");
9889
10068
  break;
9890
10069
  }
@@ -10812,62 +10991,68 @@ function _check(x) {
10812
10991
  x = y;
10813
10992
  }
10814
10993
  const mctreeTypeInfo = {
10815
- ArrayExpression: ["elements"],
10816
- AssignmentExpression: ["left", "right"],
10817
- AttributeList: ["attributes"],
10818
- Attributes: ["elements"],
10819
- BinaryExpression: ["left", "right"],
10820
- Block: [],
10821
- BlockStatement: ["body", "innerComments"],
10822
- BreakStatement: [],
10823
- CallExpression: ["callee", "arguments"],
10824
- CatchClause: ["param", "body"],
10825
- CatchClauses: ["catches"],
10826
- ClassBody: ["body"],
10827
- ClassDeclaration: ["attrs", "id", "superClass", "body"],
10828
- ClassElement: ["item"],
10829
- ConditionalExpression: ["test", "consequent", "alternate"],
10830
- ContinueStatement: [],
10831
- DoWhileStatement: ["body", "test"],
10832
- EnumDeclaration: ["attrs", "id", "body"],
10833
- EnumStringBody: ["members"],
10834
- EnumStringMember: ["id", "init"],
10835
- ExpressionStatement: ["expression"],
10836
- ForStatement: ["init", "test", "body", "update"],
10837
- FunctionDeclaration: ["attrs", "id", "params", "returnType", "body"],
10838
- Identifier: [],
10839
- IfStatement: ["test", "consequent", "alternate"],
10840
- ImportModule: ["id"],
10841
- InstanceOfCase: ["id"],
10842
- Line: [],
10843
- Literal: [],
10844
- LogicalExpression: ["left", "right"],
10845
- MemberExpression: ["object", "property"],
10846
- MethodDefinition: ["params", "returnType"],
10847
- ModuleDeclaration: ["attrs", "id", "body"],
10848
- MultiLine: [],
10849
- NewExpression: ["callee", "arguments"],
10850
- ObjectExpression: ["properties"],
10851
- ParenthesizedExpression: ["expression"],
10852
- Program: ["body", "comments"],
10853
- Property: ["key", "value"],
10854
- ReturnStatement: ["argument"],
10855
- SequenceExpression: ["expressions"],
10856
- SizedArrayExpression: ["size", "ts"],
10857
- SwitchCase: ["test", "consequent"],
10858
- SwitchStatement: ["discriminant", "cases"],
10859
- ThisExpression: [],
10860
- ThrowStatement: ["argument"],
10861
- TryStatement: ["block", "handler", "finalizer"],
10862
- TypedefDeclaration: ["attrs", "id", "ts"],
10863
- TypeSpecList: ["ts"],
10864
- TypeSpecPart: ["name", "body", "callspec", "generics"],
10865
- UnaryExpression: ["argument"],
10866
- UpdateExpression: ["argument"],
10867
- Using: ["id", "as"],
10868
- VariableDeclaration: ["attrs", "declarations"],
10869
- VariableDeclarator: ["id", "init"],
10870
- WhileStatement: ["test", "body"],
10994
+ ArrayExpression: { keys: ["elements"], expr: true },
10995
+ AssignmentExpression: { keys: ["left", "right"], expr: true },
10996
+ AttributeList: { keys: ["attributes"] },
10997
+ Attributes: { keys: ["elements"] },
10998
+ BinaryExpression: { keys: ["left", "right"], expr: true },
10999
+ Block: { keys: [] },
11000
+ BlockStatement: { keys: ["body", "innerComments"], stmt: true },
11001
+ BreakStatement: { keys: [], stmt: true },
11002
+ CallExpression: { keys: ["callee", "arguments"], expr: true },
11003
+ CatchClause: { keys: ["param", "body"] },
11004
+ CatchClauses: { keys: ["catches"] },
11005
+ ClassBody: { keys: ["body"] },
11006
+ ClassDeclaration: { keys: ["attrs", "id", "superClass", "body"], stmt: true },
11007
+ ClassElement: { keys: ["item"] },
11008
+ ConditionalExpression: {
11009
+ keys: ["test", "consequent", "alternate"],
11010
+ expr: true,
11011
+ },
11012
+ ContinueStatement: { keys: [], stmt: true },
11013
+ DoWhileStatement: { keys: ["body", "test"], stmt: true },
11014
+ EnumDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
11015
+ EnumStringBody: { keys: ["members"] },
11016
+ EnumStringMember: { keys: ["id", "init"] },
11017
+ ExpressionStatement: { keys: ["expression"], stmt: true },
11018
+ ForStatement: { keys: ["init", "test", "body", "update"], stmt: true },
11019
+ FunctionDeclaration: {
11020
+ keys: ["attrs", "id", "params", "returnType", "body"],
11021
+ stmt: true,
11022
+ },
11023
+ Identifier: { keys: [], expr: true },
11024
+ IfStatement: { keys: ["test", "consequent", "alternate"], stmt: true },
11025
+ ImportModule: { keys: ["id"] },
11026
+ InstanceOfCase: { keys: ["id"] },
11027
+ Line: { keys: [] },
11028
+ Literal: { keys: [], expr: true },
11029
+ LogicalExpression: { keys: ["left", "right"], expr: true },
11030
+ MemberExpression: { keys: ["object", "property"], expr: true },
11031
+ MethodDefinition: { keys: ["params", "returnType"] },
11032
+ ModuleDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
11033
+ MultiLine: { keys: [] },
11034
+ NewExpression: { keys: ["callee", "arguments"], expr: true },
11035
+ ObjectExpression: { keys: ["properties"], expr: true },
11036
+ ParenthesizedExpression: { keys: ["expression"], expr: true },
11037
+ Program: { keys: ["body", "comments"] },
11038
+ Property: { keys: ["key", "value"] },
11039
+ ReturnStatement: { keys: ["argument"], stmt: true },
11040
+ SequenceExpression: { keys: ["expressions"], expr: true },
11041
+ SizedArrayExpression: { keys: ["size", "ts"], expr: true },
11042
+ SwitchCase: { keys: ["test", "consequent"] },
11043
+ SwitchStatement: { keys: ["discriminant", "cases"], stmt: true },
11044
+ ThisExpression: { keys: [], expr: true },
11045
+ ThrowStatement: { keys: ["argument"], stmt: true },
11046
+ TryStatement: { keys: ["block", "handler", "finalizer"], stmt: true },
11047
+ TypedefDeclaration: { keys: ["attrs", "id", "ts"], stmt: true },
11048
+ TypeSpecList: { keys: ["ts"] },
11049
+ TypeSpecPart: { keys: ["name", "body", "callspec", "generics"] },
11050
+ UnaryExpression: { keys: ["argument"], expr: true },
11051
+ UpdateExpression: { keys: ["argument"], expr: true },
11052
+ Using: { keys: ["id", "as"] },
11053
+ VariableDeclaration: { keys: ["attrs", "declarations"], stmt: true },
11054
+ VariableDeclarator: { keys: ["id", "init"] },
11055
+ WhileStatement: { keys: ["test", "body"], stmt: true },
10871
11056
  };
10872
11057
  function isMCTreeNode(node) {
10873
11058
  return node ? typeof node === "object" && "type" in node : false;
@@ -10892,7 +11077,7 @@ function traverseAst(node, pre, post) {
10892
11077
  if (!mctreeTypeInfo[node.type]) {
10893
11078
  throw new Error("what?");
10894
11079
  }
10895
- for (const key of nodes || mctreeTypeInfo[node.type]) {
11080
+ for (const key of nodes || mctreeTypeInfo[node.type].keys) {
10896
11081
  const value = node[key];
10897
11082
  if (!value)
10898
11083
  continue;
@@ -10919,13 +11104,21 @@ function traverseAst(node, pre, post) {
10919
11104
  }
10920
11105
  }
10921
11106
  else if (isMCTreeNode(value)) {
10922
- const repl = traverseAst(value, pre, post);
11107
+ let repl = traverseAst(value, pre, post);
10923
11108
  if (repl === false) {
10924
11109
  delete node[key];
10925
11110
  }
10926
11111
  else if (repl != null) {
10927
11112
  if (Array.isArray(repl)) {
10928
- throw new Error("Array returned by traverseAst in Node context");
11113
+ if (isStatement(value) && repl.every((s) => isStatement(s))) {
11114
+ repl = withLoc({
11115
+ type: "BlockStatement",
11116
+ body: repl,
11117
+ }, repl[0], repl[repl.length - 1]);
11118
+ }
11119
+ else {
11120
+ throw new Error("Array returned by traverseAst in Node context");
11121
+ }
10929
11122
  }
10930
11123
  node[key] = repl;
10931
11124
  }
@@ -10933,6 +11126,55 @@ function traverseAst(node, pre, post) {
10933
11126
  }
10934
11127
  return post && post(node);
10935
11128
  }
11129
+ function isStatement(node) {
11130
+ return hasProperty(mctreeTypeInfo[node.type], "stmt");
11131
+ }
11132
+ function isExpression(node) {
11133
+ return hasProperty(mctreeTypeInfo[node.type], "expr");
11134
+ }
11135
+ function mayThrow(node) {
11136
+ switch (node.type) {
11137
+ case "BinaryExpression":
11138
+ case "CallExpression":
11139
+ case "ConditionalExpression":
11140
+ case "LogicalExpression":
11141
+ case "NewExpression":
11142
+ case "ThrowStatement":
11143
+ case "UnaryExpression":
11144
+ case "UpdateExpression":
11145
+ return true;
11146
+ default:
11147
+ return false;
11148
+ }
11149
+ }
11150
+ function hasProperty(obj, prop) {
11151
+ return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
11152
+ }
11153
+ function withLoc(node, start, end) {
11154
+ if (start && start.loc) {
11155
+ node.start = start.start;
11156
+ if (!node.end)
11157
+ node.end = start.end;
11158
+ node.loc = { ...(node.loc || start.loc), start: start.loc.start };
11159
+ }
11160
+ if (end && end.loc) {
11161
+ node.end = end.end;
11162
+ node.loc = { ...(node.loc || end.loc), end: end.loc.end };
11163
+ }
11164
+ return node;
11165
+ }
11166
+ function withLocDeep(node, start, end) {
11167
+ node = withLoc({ ...node }, start, end);
11168
+ for (const key of mctreeTypeInfo[node.type].keys) {
11169
+ const value = node[key];
11170
+ if (!value)
11171
+ continue;
11172
+ const fix = (v) => isMCTreeNode(v) ? withLocDeep(v, start, end) : v;
11173
+ const repl = Array.isArray(value) ? value.map(fix) : fix(value);
11174
+ node[key] = repl;
11175
+ }
11176
+ return node;
11177
+ }
10936
11178
 
10937
11179
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
10938
11180
 
@@ -11238,7 +11480,7 @@ function processInlineBody(state, func, call, root, params) {
11238
11480
  if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
11239
11481
  const ix = params[node.name];
11240
11482
  if (ix >= 0) {
11241
- replacement = call.arguments[ix];
11483
+ replacement = { ...call.arguments[ix] };
11242
11484
  replacements.add(replacement);
11243
11485
  return replacement;
11244
11486
  }
@@ -11277,6 +11519,10 @@ function processInlineBody(state, func, call, root, params) {
11277
11519
  }
11278
11520
  }
11279
11521
  function unused(expression, top) {
11522
+ const estmt = (expression) => withLoc({
11523
+ type: "ExpressionStatement",
11524
+ expression,
11525
+ }, expression);
11280
11526
  switch (expression.type) {
11281
11527
  case "Literal":
11282
11528
  return [];
@@ -11288,9 +11534,50 @@ function unused(expression, top) {
11288
11534
  if (expression.operator === "as") {
11289
11535
  return unused(expression.left);
11290
11536
  }
11291
- // fall through
11292
- case "LogicalExpression":
11293
11537
  return unused(expression.left).concat(unused(expression.right));
11538
+ case "LogicalExpression": {
11539
+ const right = unused(expression.right);
11540
+ if (!right.length)
11541
+ return unused(expression.left);
11542
+ const consequent = withLoc({
11543
+ type: "BlockStatement",
11544
+ body: [estmt(expression.right)],
11545
+ }, expression.right);
11546
+ let alternate;
11547
+ if (expression.operator == "||") {
11548
+ alternate = { ...consequent };
11549
+ consequent.body = [];
11550
+ }
11551
+ return [
11552
+ withLoc({
11553
+ type: "IfStatement",
11554
+ test: expression.left,
11555
+ consequent,
11556
+ alternate,
11557
+ }, expression),
11558
+ ];
11559
+ }
11560
+ case "ConditionalExpression": {
11561
+ const consequentExprs = unused(expression.consequent);
11562
+ const alternateExprs = unused(expression.alternate);
11563
+ if (!consequentExprs.length && !alternateExprs.length) {
11564
+ return unused(expression.test);
11565
+ }
11566
+ return [
11567
+ withLoc({
11568
+ type: "IfStatement",
11569
+ test: expression.test,
11570
+ consequent: withLoc({
11571
+ type: "BlockStatement",
11572
+ body: consequentExprs,
11573
+ }, expression.consequent),
11574
+ alternate: withLoc({
11575
+ type: "BlockStatement",
11576
+ body: alternateExprs,
11577
+ }, expression.alternate),
11578
+ }, expression),
11579
+ ];
11580
+ }
11294
11581
  case "UnaryExpression":
11295
11582
  return unused(expression.argument);
11296
11583
  case "MemberExpression":
@@ -11305,17 +11592,7 @@ function unused(expression, top) {
11305
11592
  .map((p) => unused(p.key).concat(unused(p.value)))
11306
11593
  .flat(1);
11307
11594
  }
11308
- return top
11309
- ? null
11310
- : [
11311
- {
11312
- type: "ExpressionStatement",
11313
- expression,
11314
- start: expression.start,
11315
- end: expression.end,
11316
- loc: expression.loc,
11317
- },
11318
- ];
11595
+ return top ? null : [estmt(expression)];
11319
11596
  }
11320
11597
  function diagnostic(state, loc, message, type = "INFO") {
11321
11598
  if (!loc || !loc.source)
@@ -11363,7 +11640,9 @@ function inlineWithArgs(state, func, call, context) {
11363
11640
  if (retStmtCount > 1) {
11364
11641
  inlineDiagnostic(state, func, call, "Function had more than one return statement");
11365
11642
  }
11366
- else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
11643
+ else if ((context.type === "AssignmentExpression" ||
11644
+ context.type === "VariableDeclarator") &&
11645
+ retStmtCount !== 1) {
11367
11646
  inlineDiagnostic(state, func, call, "Function did not have a return statement");
11368
11647
  return null;
11369
11648
  }
@@ -11371,7 +11650,9 @@ function inlineWithArgs(state, func, call, context) {
11371
11650
  const last = func.node.body.body.slice(-1)[0];
11372
11651
  if (!last ||
11373
11652
  last.type !== "ReturnStatement" ||
11374
- (context.type === "AssignmentExpression" && !last.argument)) {
11653
+ ((context.type === "AssignmentExpression" ||
11654
+ context.type === "VariableDeclarator") &&
11655
+ !last.argument)) {
11375
11656
  inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
11376
11657
  return null;
11377
11658
  }
@@ -11395,16 +11676,32 @@ function inlineWithArgs(state, func, call, context) {
11395
11676
  if (last.type != "ReturnStatement") {
11396
11677
  throw new Error("ReturnStatement got lost!");
11397
11678
  }
11398
- if (context.type === "AssignmentExpression") {
11399
- context.right = last.argument;
11400
- body.body[body.body.length - 1] = {
11401
- type: "ExpressionStatement",
11402
- expression: context,
11403
- };
11404
- }
11405
- else if (last.argument) {
11406
- const side_exprs = unused(last.argument);
11407
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
11679
+ if (last.argument) {
11680
+ if (context.type === "AssignmentExpression") {
11681
+ context.right = last.argument;
11682
+ body.body[body.body.length - 1] = {
11683
+ type: "ExpressionStatement",
11684
+ expression: context,
11685
+ };
11686
+ }
11687
+ else if (context.type === "VariableDeclarator") {
11688
+ const { id, init: _init, kind: _kind, ...rest } = context;
11689
+ body.body[body.body.length - 1] = {
11690
+ ...rest,
11691
+ type: "ExpressionStatement",
11692
+ expression: {
11693
+ ...rest,
11694
+ type: "AssignmentExpression",
11695
+ operator: "=",
11696
+ left: id.type === "Identifier" ? id : id.left,
11697
+ right: last.argument,
11698
+ },
11699
+ };
11700
+ }
11701
+ else {
11702
+ const side_exprs = unused(last.argument);
11703
+ body.body.splice(body.body.length - 1, 1, ...side_exprs);
11704
+ }
11408
11705
  }
11409
11706
  else {
11410
11707
  --body.body.length;
@@ -11526,89 +11823,1429 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11526
11823
  return null;
11527
11824
  }
11528
11825
 
11529
- ;// CONCATENATED MODULE: ./src/visitor.ts
11826
+ ;// CONCATENATED MODULE: ./src/control-flow.ts
11530
11827
 
11531
- function visitReferences(state, ast, name, defn, callback) {
11532
- const checkResults = ([name, results], node) => {
11533
- if (name && results) {
11534
- if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
11535
- if (callback(node, results, false) === false) {
11536
- return [];
11828
+
11829
+ const Terminals = {
11830
+ BreakStatement: "break",
11831
+ ContinueStatement: "continue",
11832
+ ReturnStatement: null,
11833
+ ThrowStatement: "throw",
11834
+ };
11835
+ class LocalState {
11836
+ constructor(func) {
11837
+ this.stack = [];
11838
+ this.info = new Map();
11839
+ this.curBlock = {};
11840
+ this.unreachable = false;
11841
+ this.push(func);
11842
+ }
11843
+ push(node) {
11844
+ const top = { node };
11845
+ this.stack.push(top);
11846
+ return top;
11847
+ }
11848
+ pop() {
11849
+ return this.stack.pop();
11850
+ }
11851
+ top(depth) {
11852
+ return this.stack[this.stack.length - (depth || 1)];
11853
+ }
11854
+ addEdge(from, to) {
11855
+ if (!from.succs) {
11856
+ from.succs = [to];
11857
+ }
11858
+ else {
11859
+ (0,external_util_cjs_namespaceObject.pushUnique)(from.succs, to);
11860
+ }
11861
+ if (!to.preds) {
11862
+ to.preds = [from];
11863
+ }
11864
+ else {
11865
+ (0,external_util_cjs_namespaceObject.pushUnique)(to.preds, from);
11866
+ }
11867
+ }
11868
+ newBlock(block) {
11869
+ if (!block)
11870
+ block = {};
11871
+ if (!this.unreachable) {
11872
+ this.addEdge(this.curBlock, block);
11873
+ }
11874
+ this.unreachable = false;
11875
+ for (let i = this.stack.length; i--;) {
11876
+ const si = this.stack[i];
11877
+ if (si.throw) {
11878
+ block.exsucc = si.throw;
11879
+ if (!si.throw.expreds) {
11880
+ si.throw.expreds = [block];
11881
+ }
11882
+ else {
11883
+ si.throw.expreds.push(block);
11537
11884
  }
11885
+ break;
11538
11886
  }
11539
11887
  }
11540
- else if (defn === false) {
11541
- if (callback(node, [], results === null) === false) {
11542
- return [];
11888
+ return (this.curBlock = block);
11889
+ }
11890
+ terminal(type) {
11891
+ const re = Terminals[type];
11892
+ if (re) {
11893
+ for (let i = this.stack.length; i--;) {
11894
+ const target = this.stack[i][re];
11895
+ if (target) {
11896
+ this.addEdge(this.curBlock, target);
11897
+ break;
11898
+ }
11543
11899
  }
11544
11900
  }
11545
- return null;
11546
- };
11547
- state.pre = (node) => {
11548
- switch (node.type) {
11549
- case "AttributeList":
11901
+ this.unreachable = true;
11902
+ }
11903
+ }
11904
+ function buildReducedGraph(state, func, notice) {
11905
+ const { stack, pre, post } = state;
11906
+ try {
11907
+ const localState = new LocalState(func.node);
11908
+ const ret = localState.curBlock;
11909
+ state.stack = func.stack;
11910
+ const stmtStack = [func.node];
11911
+ let tryActive = 0;
11912
+ state.pre = (node) => {
11913
+ if (state.inType || localState.unreachable) {
11550
11914
  return [];
11551
- case "UnaryExpression":
11552
- // a bare symbol isn't a reference
11553
- if (node.operator === ":")
11915
+ }
11916
+ if (!localState.curBlock.node &&
11917
+ (isStatement(node) || isExpression(node))) {
11918
+ localState.curBlock.node = node;
11919
+ }
11920
+ if (isStatement(node)) {
11921
+ stmtStack.push(node);
11922
+ }
11923
+ switch (node.type) {
11924
+ case "AttributeList":
11554
11925
  return [];
11555
- break;
11556
- case "BinaryExpression":
11557
- /*
11558
- * `expr has :symbol` can be treated as a reference
11559
- * to expr.symbol.
11560
- */
11561
- if (node.operator === "has") {
11562
- if (node.right.type === "UnaryExpression" &&
11563
- node.right.operator === ":") {
11564
- if (!name || node.right.argument.name === name) {
11565
- return checkResults(state.lookup({
11566
- type: "MemberExpression",
11567
- object: node.left,
11568
- property: node.right.argument,
11569
- computed: false,
11570
- }), node.right.argument);
11926
+ case "SwitchStatement": {
11927
+ const top = localState.push(node);
11928
+ top.break = {};
11929
+ state.traverse(node.discriminant);
11930
+ const testBlocks = [];
11931
+ let defaultSeen = false;
11932
+ node.cases.forEach((sc, i) => {
11933
+ if (sc.test) {
11934
+ state.traverse(sc.test);
11935
+ testBlocks[i] = localState.curBlock;
11936
+ localState.newBlock();
11571
11937
  }
11938
+ else {
11939
+ defaultSeen = true;
11940
+ }
11941
+ });
11942
+ const endOfTests = localState.curBlock;
11943
+ if (!defaultSeen) {
11944
+ localState.addEdge(endOfTests, top.break);
11572
11945
  }
11946
+ localState.unreachable = true;
11947
+ node.cases.forEach((sc, i) => {
11948
+ localState.newBlock();
11949
+ localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
11950
+ sc.consequent.every((s) => {
11951
+ state.traverse(s);
11952
+ return !localState.unreachable;
11953
+ });
11954
+ });
11955
+ localState.newBlock(top.break);
11956
+ localState.unreachable = !top.break.preds;
11957
+ return [];
11573
11958
  }
11574
- break;
11575
- case "CallExpression":
11576
- // A call expression whose callee is an identifier is looked
11577
- // up as a non-local. ie even if there's a same named local,
11578
- // it will be ignored, and the lookup will start as if the
11579
- // call had been written self.foo() rather than foo().
11580
- if (node.callee.type === "Identifier") {
11581
- if (!name || node.callee.name === name) {
11582
- /* ignore return value */
11583
- checkResults(state.lookupNonlocal(node.callee), node.callee);
11959
+ case "DoWhileStatement":
11960
+ case "WhileStatement": {
11961
+ localState.push(node);
11962
+ const top = localState.top();
11963
+ top.break = {};
11964
+ top.continue = {};
11965
+ let head;
11966
+ if (node.type === "WhileStatement") {
11967
+ head = localState.newBlock(top.continue);
11968
+ state.traverse(node.test);
11969
+ localState.addEdge(localState.newBlock(), top.break);
11584
11970
  }
11585
- return ["arguments"];
11971
+ else {
11972
+ head = localState.newBlock();
11973
+ }
11974
+ state.traverse(node.body);
11975
+ if (node.type === "DoWhileStatement") {
11976
+ localState.newBlock(top.continue);
11977
+ state.traverse(node.test);
11978
+ localState.addEdge(localState.curBlock, top.break);
11979
+ }
11980
+ localState.addEdge(localState.curBlock, head);
11981
+ localState.curBlock = top.break;
11982
+ return [];
11586
11983
  }
11587
- break;
11588
- case "Identifier":
11589
- if (!name || node.name === name) {
11590
- return checkResults(state.lookup(node), node);
11984
+ case "TryStatement": {
11985
+ const top = localState.push(node);
11986
+ const catches = (top.throw = {});
11987
+ // This edge shouldn't exist, but we can trigger
11988
+ // (incorrect) "variable may not be initialized" errors
11989
+ // in the monkey c compiler without it.
11990
+ // https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
11991
+ localState.addEdge(localState.curBlock, top.throw);
11992
+ localState.newBlock();
11993
+ tryActive++;
11994
+ state.traverse(node.block);
11995
+ tryActive--;
11996
+ delete top.throw;
11997
+ top.posttry = {};
11998
+ const tryFallsThrough = !localState.unreachable;
11999
+ if (node.finalizer) {
12000
+ tryActive++;
12001
+ top.throw = top.finally = {};
12002
+ // curBlock branches to finally, no matter how it exits.
12003
+ localState.addEdge(localState.curBlock, top.finally);
12004
+ }
12005
+ else {
12006
+ if (!localState.unreachable) {
12007
+ localState.addEdge(localState.curBlock, top.posttry);
12008
+ }
12009
+ }
12010
+ localState.unreachable = true;
12011
+ localState.newBlock(catches);
12012
+ if (node.handler) {
12013
+ state.traverse(node.handler);
12014
+ if (top.throw) {
12015
+ tryActive--;
12016
+ delete top.throw;
12017
+ }
12018
+ // Each "catch (ex instanceof Foo)" chains to the next,
12019
+ // but "catch (ex)" terminates the list. If the end
12020
+ // of the chain has a predecessor, its possible that
12021
+ // none of the conditions matched, so the exception
12022
+ // will propagate from there.
12023
+ if (localState.curBlock.preds) {
12024
+ localState.terminal("ThrowStatement");
12025
+ }
12026
+ }
12027
+ if (top.throw) {
12028
+ tryActive--;
12029
+ delete top.throw;
12030
+ }
12031
+ if (node.finalizer) {
12032
+ localState.unreachable = true;
12033
+ localState.newBlock(top.finally);
12034
+ delete top.finally;
12035
+ state.traverse(node.finalizer);
12036
+ if (tryFallsThrough && !localState.unreachable) {
12037
+ localState.addEdge(localState.curBlock, top.posttry);
12038
+ }
12039
+ localState.terminal("ThrowStatement");
12040
+ }
12041
+ localState.unreachable = true;
12042
+ localState.newBlock(top.posttry);
12043
+ return [];
11591
12044
  }
11592
- break;
11593
- case "MemberExpression":
11594
- if (!node.computed && node.property.type === "Identifier") {
11595
- if (!name || node.property.name === name) {
11596
- return checkResults(state.lookup(node), node) || ["object"];
12045
+ case "CatchClause": {
12046
+ const top = localState.top();
12047
+ if (!localState.curBlock.preds && !localState.curBlock.expreds) {
12048
+ return [];
11597
12049
  }
11598
- return ["object"];
12050
+ const next = {};
12051
+ if (node.param && node.param.type === "BinaryExpression") {
12052
+ state.traverse(node.param);
12053
+ localState.addEdge(localState.curBlock, next);
12054
+ localState.newBlock();
12055
+ }
12056
+ state.traverse(node.body);
12057
+ if (top.finally) {
12058
+ // this edge exists even if this point is unreachable
12059
+ localState.addEdge(localState.curBlock, top.finally);
12060
+ }
12061
+ if (!localState.unreachable) {
12062
+ if (!top.posttry)
12063
+ top.posttry = {};
12064
+ localState.addEdge(localState.curBlock, top.posttry);
12065
+ }
12066
+ localState.unreachable = true;
12067
+ localState.newBlock(next);
12068
+ return [];
11599
12069
  }
11600
- break;
11601
- case "MethodDefinition": {
11602
- if (!state.inType) {
11603
- throw new Error("Method definition outside of type!");
12070
+ case "ForStatement": {
12071
+ const top = localState.push(node);
12072
+ if (node.init)
12073
+ state.traverse(node.init);
12074
+ const head = localState.newBlock();
12075
+ top.break = {};
12076
+ top.continue = {};
12077
+ if (node.test) {
12078
+ state.traverse(node.test);
12079
+ localState.addEdge(localState.curBlock, top.break);
12080
+ localState.newBlock();
12081
+ }
12082
+ state.traverse(node.body);
12083
+ localState.newBlock(top.continue);
12084
+ if (node.update) {
12085
+ state.traverse(node.update);
12086
+ }
12087
+ if (!localState.unreachable) {
12088
+ localState.addEdge(localState.curBlock, head);
12089
+ }
12090
+ // there is no fall through from the end of the loop
12091
+ // to the next block. The only way there is via break
12092
+ // or the test failing.
12093
+ localState.unreachable = true;
12094
+ localState.newBlock(top.break);
12095
+ if (!top.break.preds) {
12096
+ localState.unreachable = true;
12097
+ }
12098
+ return [];
11604
12099
  }
11605
- if (node.params) {
11606
- node.params.forEach((param) => {
11607
- if (param.type == "BinaryExpression") {
11608
- state.traverse(param.right);
11609
- state.inType = true;
12100
+ case "IfStatement":
12101
+ case "ConditionalExpression": {
12102
+ state.traverse(node.test);
12103
+ const alternate = {};
12104
+ localState.addEdge(localState.curBlock, alternate);
12105
+ localState.newBlock();
12106
+ state.traverse(node.consequent);
12107
+ const consequent = localState.unreachable
12108
+ ? null
12109
+ : localState.curBlock;
12110
+ localState.unreachable = true;
12111
+ localState.newBlock(alternate);
12112
+ if (node.alternate) {
12113
+ state.traverse(node.alternate);
12114
+ if (!localState.unreachable) {
12115
+ localState.newBlock();
11610
12116
  }
11611
- });
12117
+ }
12118
+ if (consequent) {
12119
+ if (localState.unreachable) {
12120
+ localState.newBlock();
12121
+ }
12122
+ localState.addEdge(consequent, localState.curBlock);
12123
+ }
12124
+ return [];
12125
+ }
12126
+ case "LogicalExpression": {
12127
+ state.traverse(node.left);
12128
+ if (localState.unreachable)
12129
+ break;
12130
+ const mid = localState.curBlock;
12131
+ localState.newBlock();
12132
+ state.traverse(node.right);
12133
+ localState.newBlock();
12134
+ localState.addEdge(mid, localState.curBlock);
12135
+ return [];
12136
+ }
12137
+ case "VariableDeclarator":
12138
+ return ["init"];
12139
+ case "MemberExpression":
12140
+ if (!node.computed) {
12141
+ return ["object"];
12142
+ }
12143
+ break;
12144
+ case "UnaryExpression":
12145
+ if (node.operator === ":") {
12146
+ return [];
12147
+ }
12148
+ break;
12149
+ case "UpdateExpression":
12150
+ // We don't want to traverse the argument, since then it would
12151
+ // look like a ref, rather than a def. But if its a
12152
+ // MemberExpression, we *do* want to traverse the subexpressions
12153
+ // as potential refs.
12154
+ if (node.argument.type === "MemberExpression") {
12155
+ state.traverse(node.argument.object);
12156
+ if (node.argument.computed) {
12157
+ state.traverse(node.argument.property);
12158
+ }
12159
+ }
12160
+ return [];
12161
+ case "AssignmentExpression":
12162
+ if (node.left.type === "MemberExpression") {
12163
+ state.traverse(node.left.object);
12164
+ if (node.left.computed) {
12165
+ state.traverse(node.left.property);
12166
+ }
12167
+ }
12168
+ return ["right"];
12169
+ case "ThrowStatement":
12170
+ case "ReturnStatement":
12171
+ if (node.argument) {
12172
+ state.traverse(node.argument);
12173
+ }
12174
+ // fall through
12175
+ case "BreakStatement":
12176
+ case "ContinueStatement":
12177
+ localState.terminal(node.type);
12178
+ return [];
12179
+ }
12180
+ return null;
12181
+ };
12182
+ const addEvent = (block, event) => {
12183
+ if (!block.events) {
12184
+ block.events = [event];
12185
+ }
12186
+ else {
12187
+ block.events.push(event);
12188
+ }
12189
+ };
12190
+ state.post = (node) => {
12191
+ const curStmt = stmtStack[stmtStack.length - 1];
12192
+ if (!state.inType) {
12193
+ const throws = tryActive > 0 && mayThrow(node);
12194
+ const event = notice(node, curStmt, throws);
12195
+ if (throws) {
12196
+ if (!event) {
12197
+ throw new Error("mayThrow expression in try/catch must generate an event");
12198
+ }
12199
+ }
12200
+ else if (event) {
12201
+ event.mayThrow = false;
12202
+ }
12203
+ if (event) {
12204
+ if (event.mayThrow) {
12205
+ for (let i = localState.stack.length; i--;) {
12206
+ const target = localState.stack[i].throw;
12207
+ if (target) {
12208
+ if (localState.curBlock.exsucc) {
12209
+ if (localState.curBlock.exsucc !== target) {
12210
+ throw new Error(`Block has multiple throw targets`);
12211
+ }
12212
+ }
12213
+ else {
12214
+ localState.curBlock.exsucc = target;
12215
+ if (!target.expreds) {
12216
+ target.expreds = [localState.curBlock];
12217
+ }
12218
+ else {
12219
+ target.expreds.push(localState.curBlock);
12220
+ }
12221
+ }
12222
+ break;
12223
+ }
12224
+ }
12225
+ }
12226
+ addEvent(localState.curBlock, event);
12227
+ }
12228
+ }
12229
+ if (curStmt === node) {
12230
+ stmtStack.pop();
12231
+ }
12232
+ if (localState.top().node === node) {
12233
+ localState.pop();
12234
+ }
12235
+ return null;
12236
+ };
12237
+ state.traverse(func.node);
12238
+ return cleanCfg(ret);
12239
+ }
12240
+ finally {
12241
+ state.pre = pre;
12242
+ state.post = post;
12243
+ state.stack = stack;
12244
+ }
12245
+ }
12246
+ function cleanCfg(head) {
12247
+ preOrderTraverse(head, (cur) => {
12248
+ if (cur.succs && cur.succs.length === 1) {
12249
+ const succ = cur.succs[0];
12250
+ if (succ !== head &&
12251
+ succ.preds.length === 1 &&
12252
+ (!cur.exsucc || cur.exsucc === succ.exsucc) &&
12253
+ (!succ.succs ||
12254
+ succ.succs.length === 1 ||
12255
+ (cur.preds && cur.preds.length === 1))) {
12256
+ if (cur.events) {
12257
+ if (succ.events) {
12258
+ cur.events.push(...succ.events);
12259
+ }
12260
+ }
12261
+ else if (succ.events) {
12262
+ cur.events = succ.events;
12263
+ }
12264
+ if (succ.exsucc) {
12265
+ const preds = succ.exsucc.expreds;
12266
+ for (let i = preds.length; i--;) {
12267
+ if (preds[i] === succ) {
12268
+ // If cur has an exsucc, we already
12269
+ // checked that its the same as succ's,
12270
+ // so we can just delete the edge.
12271
+ // Otherwise, we need to point it at cur.
12272
+ if (cur.exsucc) {
12273
+ preds.splice(i, 1);
12274
+ }
12275
+ else {
12276
+ preds[i] = cur;
12277
+ }
12278
+ }
12279
+ }
12280
+ }
12281
+ cur.exsucc = succ.exsucc;
12282
+ cur.succs = succ.succs;
12283
+ if (cur.succs) {
12284
+ cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
12285
+ if (p === succ) {
12286
+ arr[i] = cur;
12287
+ }
12288
+ }));
12289
+ }
12290
+ }
12291
+ }
12292
+ });
12293
+ return head;
12294
+ }
12295
+ function postOrderTraverse(head, visitor) {
12296
+ const visited = new Set();
12297
+ const helper = (cur) => {
12298
+ if (visited.has(cur))
12299
+ return;
12300
+ visited.add(cur);
12301
+ if (cur.succs) {
12302
+ cur.succs.forEach((block) => helper(block));
12303
+ }
12304
+ if (cur.exsucc)
12305
+ helper(cur.exsucc);
12306
+ visitor(cur);
12307
+ };
12308
+ helper(head);
12309
+ }
12310
+ function preOrderTraverse(head, visitor) {
12311
+ const visited = new Set();
12312
+ const helper = (cur) => {
12313
+ if (visited.has(cur))
12314
+ return;
12315
+ visited.add(cur);
12316
+ visitor(cur);
12317
+ if (cur.succs) {
12318
+ cur.succs.forEach((block) => helper(block));
12319
+ }
12320
+ if (cur.exsucc)
12321
+ helper(cur.exsucc);
12322
+ };
12323
+ helper(head);
12324
+ }
12325
+ function getPostOrder(head) {
12326
+ const blocks = [];
12327
+ postOrderTraverse(head, (block) => blocks.push(block));
12328
+ return blocks;
12329
+ }
12330
+ function getPreOrder(head) {
12331
+ const blocks = [];
12332
+ postOrderTraverse(head, (block) => blocks.push(block));
12333
+ return blocks;
12334
+ }
12335
+
12336
+ // EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
12337
+ var priorityqueuejs = __webpack_require__(2789);
12338
+ ;// CONCATENATED MODULE: ./src/pre.ts
12339
+
12340
+
12341
+
12342
+
12343
+ /**
12344
+ * This implements a pseudo Partial Redundancy Elimination
12345
+ * pass. It isn't quite like traditional PRE because we're
12346
+ * aiming to minimize size, not dynamic instructions. So
12347
+ * for us, its worthwhile to take something like:
12348
+ *
12349
+ * switch (x) {
12350
+ * case 1: foo(A.B); break;
12351
+ * case 2: foo(C); break;
12352
+ * case 3: bar(A.B); break;
12353
+ * }
12354
+ *
12355
+ * and rewrite it as
12356
+ *
12357
+ * var tmp = A.B;
12358
+ * switch (x) {
12359
+ * case 1: foo(tmp); break;
12360
+ * case 2: foo(C); break;
12361
+ * case 3: bar(tmp); break;
12362
+ * }
12363
+ *
12364
+ * because even though A.B wasn't used on all paths where we
12365
+ * inserted the temporary, we still reduced the code size.
12366
+ */
12367
+ const logging = false;
12368
+ function declFullName(decl) {
12369
+ switch (decl.type) {
12370
+ case "Literal":
12371
+ return decl.raw || decl.value?.toString() || "null";
12372
+ case "VariableDeclarator":
12373
+ return decl.fullName;
12374
+ default:
12375
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12376
+ }
12377
+ }
12378
+ function declName(decl) {
12379
+ switch (decl.type) {
12380
+ case "Literal":
12381
+ return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
12382
+ case "VariableDeclarator":
12383
+ return decl.name;
12384
+ default:
12385
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12386
+ }
12387
+ }
12388
+ function sizeBasedPRE(state, func) {
12389
+ if (!func.node.body)
12390
+ return;
12391
+ if (!state.config ||
12392
+ !state.config.sizeBasedPRE ||
12393
+ (typeof state.config.sizeBasedPRE === "string" &&
12394
+ state.config.sizeBasedPRE !== func.fullName)) {
12395
+ return;
12396
+ }
12397
+ const { graph: head, identifiers } = buildPREGraph(state, func);
12398
+ const candidates = computeAttributes(head);
12399
+ if (candidates) {
12400
+ if (logging) {
12401
+ console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
12402
+ candidates.forEach((s, decl) => {
12403
+ const defs = Array.from(s.ant).reduce((defs, event) => {
12404
+ if (event.type === "def")
12405
+ defs++;
12406
+ return defs;
12407
+ }, 0);
12408
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
12409
+ });
12410
+ }
12411
+ const nodeMap = new Map();
12412
+ const declMap = new Map();
12413
+ const variableDecl = withLoc({
12414
+ type: "VariableDeclaration",
12415
+ declarations: [],
12416
+ kind: "var",
12417
+ }, func.node.body);
12418
+ variableDecl.end = variableDecl.start;
12419
+ variableDecl.loc.end = variableDecl.loc.start;
12420
+ candidates.forEach((s, decl) => {
12421
+ let name;
12422
+ let i = 0;
12423
+ do {
12424
+ name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
12425
+ if (!identifiers.has(name))
12426
+ break;
12427
+ i++;
12428
+ } while (true);
12429
+ declMap.set(decl, name);
12430
+ variableDecl.declarations.push(withLoc({
12431
+ type: "VariableDeclarator",
12432
+ id: withLoc({ type: "Identifier", name }, variableDecl),
12433
+ kind: "var",
12434
+ }, variableDecl));
12435
+ s.ant.forEach((event) => {
12436
+ const events = nodeMap.get(event.node);
12437
+ if (!events) {
12438
+ nodeMap.set(event.node, [event]);
12439
+ }
12440
+ else {
12441
+ events.push(event);
12442
+ }
12443
+ });
12444
+ });
12445
+ applyReplacements(func.node, nodeMap, declMap);
12446
+ func.node.body.body.unshift(variableDecl);
12447
+ }
12448
+ }
12449
+ function unhandledExpression(node) {
12450
+ throw new Error(`Unhandled expression type: ${node.type}`);
12451
+ }
12452
+ function buildPREGraph(state, func) {
12453
+ const findDecl = (node) => {
12454
+ if (node.type === "Identifier" ||
12455
+ (node.type === "MemberExpression" && !node.computed)) {
12456
+ const [, results] = state.lookup(node);
12457
+ if (results &&
12458
+ results.length === 1 &&
12459
+ results[0].parent?.type != "BlockStatement" &&
12460
+ results[0].results.length === 1 &&
12461
+ results[0].results[0].type === "VariableDeclarator") {
12462
+ return results[0].results[0];
12463
+ }
12464
+ }
12465
+ return null;
12466
+ };
12467
+ const literals = new Map();
12468
+ const identifiers = new Set();
12469
+ const liveDefs = new Map();
12470
+ const liveStmts = new Map();
12471
+ const liveDef = (def, stmt) => {
12472
+ let curNodes = liveDefs.get(def);
12473
+ if (!curNodes) {
12474
+ liveDefs.set(def, (curNodes = new Set()));
12475
+ }
12476
+ curNodes.add(stmt);
12477
+ let defs = liveStmts.get(stmt);
12478
+ if (!defs) {
12479
+ liveStmts.set(stmt, (defs = new Map()));
12480
+ }
12481
+ defs.set(def, (defs.get(def) || 0) + 1);
12482
+ };
12483
+ return {
12484
+ identifiers,
12485
+ graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
12486
+ const defs = liveStmts.get(node);
12487
+ if (defs) {
12488
+ liveStmts.delete(node);
12489
+ defs.forEach((count, def) => {
12490
+ if (count > 1) {
12491
+ defs.set(def, count--);
12492
+ return;
12493
+ }
12494
+ const v = liveDefs.get(def);
12495
+ if (!v || !v.has(node)) {
12496
+ throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
12497
+ }
12498
+ v.delete(node);
12499
+ if (!v.size) {
12500
+ liveDefs.delete(def);
12501
+ }
12502
+ });
12503
+ }
12504
+ switch (node.type) {
12505
+ case "BinaryExpression":
12506
+ case "UnaryExpression":
12507
+ case "SizedArrayExpression":
12508
+ case "ArrayExpression":
12509
+ case "ObjectExpression":
12510
+ case "ThisExpression":
12511
+ case "LogicalExpression":
12512
+ case "ConditionalExpression":
12513
+ case "SequenceExpression":
12514
+ case "ParenthesizedExpression":
12515
+ break;
12516
+ case "Literal":
12517
+ if (!node.value && refCost(node) > LocalRefCost) {
12518
+ let decl = literals.get(node.value);
12519
+ if (!decl) {
12520
+ decl = node;
12521
+ literals.set(node.value, decl);
12522
+ }
12523
+ return {
12524
+ type: "ref",
12525
+ node,
12526
+ decl: decl,
12527
+ mayThrow,
12528
+ };
12529
+ }
12530
+ break;
12531
+ case "Identifier":
12532
+ identifiers.add(node.name);
12533
+ // fall through
12534
+ case "MemberExpression":
12535
+ {
12536
+ const decl = findDecl(node);
12537
+ if (decl && decl.type === "VariableDeclarator") {
12538
+ const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
12539
+ liveDefs.get(decl);
12540
+ if (defStmts) {
12541
+ break;
12542
+ /*
12543
+ // hold off on this for now. we need to communicate
12544
+ // which defs need to be fixed, which involves yet-another
12545
+ // table.
12546
+
12547
+ if (defStmts.size !== 1) break;
12548
+ const fixable = isFixableStmt([...defStmts][0]);
12549
+ if (fixable === false) break;
12550
+ cost += fixable;
12551
+ */
12552
+ }
12553
+ return {
12554
+ type: "ref",
12555
+ node,
12556
+ decl,
12557
+ mayThrow,
12558
+ };
12559
+ }
12560
+ }
12561
+ break;
12562
+ case "VariableDeclarator": {
12563
+ const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
12564
+ if (decl) {
12565
+ liveDef(decl, stmt);
12566
+ return {
12567
+ type: "def",
12568
+ node,
12569
+ decl,
12570
+ mayThrow,
12571
+ };
12572
+ }
12573
+ break;
12574
+ }
12575
+ case "AssignmentExpression": {
12576
+ const decl = findDecl(node.left);
12577
+ if (decl) {
12578
+ liveDef(decl, stmt);
12579
+ return {
12580
+ type: "def",
12581
+ node,
12582
+ decl,
12583
+ mayThrow,
12584
+ };
12585
+ }
12586
+ break;
12587
+ }
12588
+ case "UpdateExpression": {
12589
+ const decl = findDecl(node.argument);
12590
+ if (decl) {
12591
+ liveDef(decl, stmt);
12592
+ return {
12593
+ type: "def",
12594
+ node,
12595
+ decl,
12596
+ mayThrow,
12597
+ };
12598
+ }
12599
+ break;
12600
+ }
12601
+ case "NewExpression":
12602
+ case "CallExpression":
12603
+ liveDef(null, stmt);
12604
+ return { type: "mod", node, mayThrow };
12605
+ default:
12606
+ if (!isExpression(node))
12607
+ break;
12608
+ unhandledExpression(node);
12609
+ }
12610
+ if (mayThrow) {
12611
+ return { type: "exn", node, mayThrow };
12612
+ }
12613
+ return null;
12614
+ }),
12615
+ };
12616
+ }
12617
+ function anticipatedDecls() {
12618
+ return new Map();
12619
+ }
12620
+ function cloneSet(ae) {
12621
+ return new Set(ae);
12622
+ }
12623
+ function mergeSet(a, b) {
12624
+ b.forEach((event) => a.add(event));
12625
+ }
12626
+ function equalSet(a, b) {
12627
+ if (a.size != b.size)
12628
+ return false;
12629
+ for (const item of a) {
12630
+ if (!b.has(item))
12631
+ return false;
12632
+ }
12633
+ return true;
12634
+ }
12635
+ function equalMap(a, b) {
12636
+ if (a.size != b.size)
12637
+ return false;
12638
+ for (const [item, value] of a) {
12639
+ if (b.get(item) !== value)
12640
+ return false;
12641
+ }
12642
+ return true;
12643
+ }
12644
+ function anticipatedState(node, events) {
12645
+ return { ant: events || new Set(), live: true, node, members: new Map() };
12646
+ }
12647
+ function cloneAnticipatedState(as) {
12648
+ return {
12649
+ ant: cloneSet(as.ant),
12650
+ live: as.live,
12651
+ node: as.node,
12652
+ members: new Map(as.members),
12653
+ };
12654
+ }
12655
+ function cloneAnticipatedDecls(ad) {
12656
+ const copy = anticipatedDecls();
12657
+ for (const [k, v] of ad) {
12658
+ copy.set(k, cloneAnticipatedState(v));
12659
+ }
12660
+ return copy;
12661
+ }
12662
+ function mergeAnticipatedDecls(a, b) {
12663
+ for (const [k, v] of b) {
12664
+ const ae = a.get(k);
12665
+ if (ae) {
12666
+ mergeSet(ae.ant, v.ant);
12667
+ v.members.forEach((live, block) => ae.members.set(block, live));
12668
+ if (v.live)
12669
+ ae.live = true;
12670
+ }
12671
+ else {
12672
+ a.set(k, cloneAnticipatedState(v));
12673
+ }
12674
+ }
12675
+ }
12676
+ function equalStates(a, b) {
12677
+ if (a.size !== b.size)
12678
+ return false;
12679
+ for (const [k, ae] of a) {
12680
+ const be = b.get(k);
12681
+ if (!be ||
12682
+ be.live != ae.live ||
12683
+ !equalSet(ae.ant, be.ant) ||
12684
+ !equalMap(ae.members, be.members)) {
12685
+ return false;
12686
+ }
12687
+ }
12688
+ return true;
12689
+ }
12690
+ const LocalRefCost = 2;
12691
+ function refCost(node) {
12692
+ if (node.type === "Literal") {
12693
+ switch (typeof node.value) {
12694
+ case "string":
12695
+ return 5;
12696
+ case "number":
12697
+ return 5;
12698
+ case "boolean":
12699
+ return 2;
12700
+ default:
12701
+ if (node.value === null) {
12702
+ return 2;
12703
+ }
12704
+ return 0;
12705
+ }
12706
+ }
12707
+ // A read from a non-local identifier takes 8 bytes
12708
+ let cost = 8;
12709
+ if (node.type === "Identifier")
12710
+ return cost;
12711
+ while (true) {
12712
+ const next = node.object;
12713
+ if (next.type != "MemberExpression") {
12714
+ if (next.type != "ThisExpression") {
12715
+ cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
12716
+ }
12717
+ return cost;
12718
+ }
12719
+ node = next;
12720
+ cost += 6;
12721
+ }
12722
+ }
12723
+ function defCost(node) {
12724
+ return refCost(node) + 2;
12725
+ }
12726
+ function candidateBoundary(candState) {
12727
+ const boundary = new Set();
12728
+ candState.members.forEach((live, block) => {
12729
+ if (live && block !== candState.head) {
12730
+ if (block.preds) {
12731
+ block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
12732
+ }
12733
+ }
12734
+ });
12735
+ if (candState.live) {
12736
+ if (!candState.head) {
12737
+ throw new Error(`Missing head`);
12738
+ }
12739
+ boundary.add(candState.head);
12740
+ }
12741
+ return boundary;
12742
+ }
12743
+ function candidateCost(candState) {
12744
+ let cost = 0;
12745
+ candState.ant.forEach((event) => {
12746
+ if (event.type === "ref") {
12747
+ cost -= refCost(candState.node) - LocalRefCost;
12748
+ }
12749
+ else {
12750
+ cost += defCost(candState.node);
12751
+ }
12752
+ });
12753
+ cost += defCost(candState.node) * candidateBoundary(candState).size;
12754
+ return cost;
12755
+ }
12756
+ function computeAttributes(head) {
12757
+ const order = getPostOrder(head);
12758
+ order.forEach((block, i) => {
12759
+ block.order = i;
12760
+ });
12761
+ if (logging) {
12762
+ order.forEach((block) => {
12763
+ console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
12764
+ .map((block) => block.order)
12765
+ .join(", ")}`);
12766
+ if (block.events) {
12767
+ block.events.forEach((event) => event.type !== "exn" &&
12768
+ console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
12769
+ }
12770
+ console.log(`Succs: ${(block.succs || [])
12771
+ .map((block) => block.order)
12772
+ .join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
12773
+ });
12774
+ }
12775
+ const enqueued = new Set();
12776
+ const queue = new priorityqueuejs((b, a) => (a.order || 0) - (b.order || 0));
12777
+ const enqueue = (block) => {
12778
+ if (!enqueued.has(block)) {
12779
+ enqueued.add(block);
12780
+ queue.enq(block);
12781
+ }
12782
+ };
12783
+ const dequeue = () => {
12784
+ const block = queue.deq();
12785
+ enqueued.delete(block);
12786
+ return block;
12787
+ };
12788
+ const blockStates = [];
12789
+ /*
12790
+ Algorithm
12791
+ =========
12792
+
12793
+ Process blocks in post-order, and the events in reverse
12794
+ order to collect the AnticipatedState at the start of each
12795
+ Block.
12796
+
12797
+ Then for each EventDecl find the best starting block.
12798
+ */
12799
+ const modMap = new Map();
12800
+ const getMod = (event, decl, id) => {
12801
+ if (id.type !== "Identifier" && id.type !== "MemberExpression") {
12802
+ throw new Error("Trying to modify a non-variable");
12803
+ }
12804
+ let eventMap = modMap.get(event);
12805
+ if (!eventMap) {
12806
+ modMap.set(event, (eventMap = new Map()));
12807
+ }
12808
+ let result = eventMap.get(decl);
12809
+ if (!result) {
12810
+ result = {
12811
+ type: "mod",
12812
+ node: event.node,
12813
+ decl,
12814
+ id,
12815
+ mayThrow: event.mayThrow,
12816
+ };
12817
+ eventMap.set(decl, result);
12818
+ }
12819
+ return result;
12820
+ };
12821
+ order.forEach((block) => enqueue(block));
12822
+ while (queue.size()) {
12823
+ const top = dequeue();
12824
+ if (top.order === undefined) {
12825
+ throw new Error(`Unreachable block was visited!`);
12826
+ }
12827
+ const curState = (top.succs &&
12828
+ top.succs.reduce((blockState, succ) => {
12829
+ const succState = blockStates[succ.order];
12830
+ if (succState) {
12831
+ if (!blockState) {
12832
+ blockState = cloneAnticipatedDecls(succState);
12833
+ }
12834
+ else {
12835
+ mergeAnticipatedDecls(blockState, succState);
12836
+ }
12837
+ }
12838
+ return blockState;
12839
+ }, null)) ||
12840
+ anticipatedDecls();
12841
+ if (top.events) {
12842
+ for (let i = top.events.length; i--;) {
12843
+ const event = top.events[i];
12844
+ if (event.mayThrow && top.exsucc) {
12845
+ const succState = blockStates[top.exsucc.order];
12846
+ if (succState) {
12847
+ mergeAnticipatedDecls(curState, succState);
12848
+ }
12849
+ }
12850
+ switch (event.type) {
12851
+ case "ref": {
12852
+ let candidates = curState.get(event.decl);
12853
+ if (!candidates) {
12854
+ candidates = anticipatedState(event.node);
12855
+ curState.set(event.decl, candidates);
12856
+ }
12857
+ candidates.ant.add(event);
12858
+ candidates.live = true;
12859
+ break;
12860
+ }
12861
+ case "mod": {
12862
+ curState.forEach((candidates, decl) => {
12863
+ if (decl.type === "VariableDeclarator" &&
12864
+ decl.node.kind === "var" &&
12865
+ candidates.live) {
12866
+ candidates.ant.add(getMod(event, decl, candidates.node));
12867
+ candidates.live = false;
12868
+ }
12869
+ });
12870
+ break;
12871
+ }
12872
+ case "def": {
12873
+ let candidates = curState.get(event.decl);
12874
+ const isUpdate = event.node.type === "UpdateExpression" ||
12875
+ (event.node.type === "AssignmentExpression" &&
12876
+ event.node.operator !== "=");
12877
+ if (!candidates) {
12878
+ const target = event.node.type === "AssignmentExpression"
12879
+ ? event.node.left
12880
+ : event.node.type === "UpdateExpression"
12881
+ ? event.node.argument
12882
+ : event.node.id.type === "BinaryExpression"
12883
+ ? event.node.id.left
12884
+ : event.node.id;
12885
+ candidates = anticipatedState(target);
12886
+ curState.set(event.decl, candidates);
12887
+ }
12888
+ if (isUpdate || candidates.live) {
12889
+ candidates.ant.add(event);
12890
+ }
12891
+ if (!isUpdate) {
12892
+ candidates.live = false;
12893
+ }
12894
+ break;
12895
+ }
12896
+ }
12897
+ }
12898
+ }
12899
+ curState.forEach((antState) => {
12900
+ antState.head = top;
12901
+ antState.members.set(top, antState.live);
12902
+ });
12903
+ const oldState = blockStates[top.order];
12904
+ if (oldState && equalStates(oldState, curState)) {
12905
+ continue;
12906
+ }
12907
+ blockStates[top.order] = curState;
12908
+ if (top.preds) {
12909
+ top.preds.forEach((pred) => enqueue(pred));
12910
+ }
12911
+ }
12912
+ const candidateDecls = anticipatedDecls();
12913
+ blockStates.forEach((blockState, i) => {
12914
+ blockState &&
12915
+ blockState.forEach((events, decl) => {
12916
+ const cost = candidateCost(events);
12917
+ if (cost >= 0)
12918
+ return;
12919
+ const existing = candidateDecls.get(decl);
12920
+ if (!existing || candidateCost(existing) > cost) {
12921
+ const boundary = candidateBoundary(events);
12922
+ if (!Array.from(boundary).every((block) => {
12923
+ if (block !== events.head && block.events) {
12924
+ if (events.node.type === "Literal") {
12925
+ return false;
12926
+ }
12927
+ let i = block.events.length;
12928
+ while (i--) {
12929
+ const event = block.events[i];
12930
+ if (event.type === "def" || event.type === "mod") {
12931
+ events.ant.add({
12932
+ type: "mod",
12933
+ node: event.node,
12934
+ decl,
12935
+ id: events.node,
12936
+ mayThrow: false,
12937
+ });
12938
+ events.members.set(block, false);
12939
+ return true;
12940
+ }
12941
+ }
12942
+ }
12943
+ const node = block.node;
12944
+ if (!node)
12945
+ return false;
12946
+ events.ant.add({
12947
+ type: "mod",
12948
+ node: node.type === "FunctionDeclaration" ? node.body : node,
12949
+ before: true,
12950
+ decl,
12951
+ id: events.node,
12952
+ mayThrow: false,
12953
+ });
12954
+ events.members.set(block, false);
12955
+ return true;
12956
+ })) {
12957
+ return;
12958
+ }
12959
+ events.live = false;
12960
+ if (candidateCost(events) != cost) {
12961
+ throw new Error(`cost of block ${i} changed`);
12962
+ }
12963
+ candidateDecls.set(decl, events);
12964
+ }
12965
+ });
12966
+ });
12967
+ if (candidateDecls.size) {
12968
+ return candidateDecls;
12969
+ }
12970
+ return null;
12971
+ }
12972
+ /*
12973
+ * Determine the cost of fixing a def under a statement.
12974
+ *
12975
+ * eg:
12976
+ *
12977
+ * if (foo()) {
12978
+ * bar(X.y);
12979
+ * } else {
12980
+ * baz(X.y);
12981
+ * }
12982
+ *
12983
+ * Here, we could pull out X.y as a local, but if foo might modify
12984
+ * X.y, we have nowhere to insert the temporary. But we can rewrite
12985
+ * it as:
12986
+ *
12987
+ * var tmp = foo();
12988
+ * if (tmp) {
12989
+ * bar(X.y);
12990
+ * } else {
12991
+ * baz(X.y);
12992
+ * }
12993
+ *
12994
+ * and now we can insert a temporary before the if, but it costs
12995
+ * 4 bytes to do so.
12996
+ *
12997
+ * We can do the same for switch statements unless (ugh!)
12998
+ * the cases might modify the decl too.
12999
+ *
13000
+ * eg
13001
+ *
13002
+ * switch (foo()) {
13003
+ * case bar(): ...
13004
+ * }
13005
+ *
13006
+ */
13007
+ function _isFixableStmt(node) {
13008
+ switch (node.type) {
13009
+ case "IfStatement":
13010
+ return 4;
13011
+ case "SwitchStatement":
13012
+ if (node.cases.every((c) => !c.test ||
13013
+ c.test.type === "Literal" ||
13014
+ c.test.type === "Identifier" ||
13015
+ c.test.type === "InstanceOfCase" ||
13016
+ (c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
13017
+ return 4;
13018
+ }
13019
+ break;
13020
+ }
13021
+ return false;
13022
+ }
13023
+ function applyReplacements(func, nodeMap, declMap) {
13024
+ const ident = (name, node) => {
13025
+ return withLoc({ type: "Identifier", name }, node);
13026
+ };
13027
+ const pendingMap = new Map();
13028
+ const stmtStack = [func];
13029
+ traverseAst(func, (node) => {
13030
+ if (isStatement(node)) {
13031
+ stmtStack.push(node);
13032
+ }
13033
+ }, (node) => {
13034
+ const stmt = stmtStack[stmtStack.length - 1];
13035
+ if (stmt === node)
13036
+ stmtStack.pop();
13037
+ const events = nodeMap.get(node);
13038
+ if (events) {
13039
+ if (events.length === 1) {
13040
+ if (events[0].type === "ref") {
13041
+ if (node.type !== "Identifier" &&
13042
+ node.type !== "MemberExpression" &&
13043
+ node.type !== "Literal") {
13044
+ throw new Error(`Ref found, but wrong type of node: ${node.type}`);
13045
+ }
13046
+ const name = declMap.get(events[0].decl);
13047
+ if (!name) {
13048
+ throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(node)}"`);
13049
+ }
13050
+ return ident(name, node);
13051
+ }
13052
+ else if (events[0].type === "def") {
13053
+ if (node.type !== "AssignmentExpression" &&
13054
+ node.type !== "UpdateExpression") {
13055
+ throw new Error(`Def found, but wrong type of node: ${node.type}`);
13056
+ }
13057
+ const target = node.type === "AssignmentExpression"
13058
+ ? node.left
13059
+ : node.argument;
13060
+ const name = declMap.get(events[0].decl);
13061
+ if (!name) {
13062
+ throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(target)}"`);
13063
+ }
13064
+ const id = ident(name, target);
13065
+ const assign = withLoc({
13066
+ type: "AssignmentExpression",
13067
+ left: target,
13068
+ right: { ...id },
13069
+ operator: "=",
13070
+ }, node);
13071
+ if (node.type === "AssignmentExpression") {
13072
+ node.left = id;
13073
+ }
13074
+ else {
13075
+ node.argument = id;
13076
+ }
13077
+ return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
13078
+ }
13079
+ }
13080
+ events.forEach((event) => {
13081
+ if (event.type !== "mod") {
13082
+ throw new Error(`Unexpected ${event.type} found amongst multiple events`);
13083
+ }
13084
+ if (!event.decl) {
13085
+ throw new Error(`Unexpected null decl on mod event`);
13086
+ }
13087
+ let pending = pendingMap.get(stmt);
13088
+ if (!pending) {
13089
+ pendingMap.set(stmt, (pending = new Set()));
13090
+ }
13091
+ pending.add(event);
13092
+ });
13093
+ }
13094
+ const pending = pendingMap.get(node);
13095
+ if (node.type === "SequenceExpression") {
13096
+ if (pending) {
13097
+ throw new Error(`Unexpected pending list at SequenceExpression`);
13098
+ }
13099
+ for (let i = node.expressions.length; i--;) {
13100
+ const ni = node.expressions[i];
13101
+ if (ni.type === "SequenceExpression") {
13102
+ node.expressions.splice(i, 1, ...ni.expressions);
13103
+ }
13104
+ }
13105
+ }
13106
+ const applyPending = (results, locNode) => {
13107
+ const target = results.length === 1 && results[0].type === "BlockStatement"
13108
+ ? results[0]
13109
+ : null;
13110
+ pendingMap.delete(node);
13111
+ pending.forEach((event) => {
13112
+ const decl = event.decl;
13113
+ const name = declMap.get(decl);
13114
+ if (!name) {
13115
+ throw new Error(`No replacement found for "${declFullName(decl)}"`);
13116
+ }
13117
+ if (!event.id) {
13118
+ throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
13119
+ }
13120
+ const rhs = withLocDeep(event.id, locNode, locNode);
13121
+ rhs.end = rhs.start;
13122
+ if (rhs.loc) {
13123
+ rhs.loc.end = rhs.loc.start;
13124
+ }
13125
+ const insertion = withLoc({
13126
+ type: "ExpressionStatement",
13127
+ expression: withLoc({
13128
+ type: "AssignmentExpression",
13129
+ left: ident(name, rhs),
13130
+ right: rhs,
13131
+ operator: "=",
13132
+ }, rhs),
13133
+ }, rhs);
13134
+ if (event.type === "mod" && event.before) {
13135
+ if (target) {
13136
+ target.body.unshift(insertion);
13137
+ }
13138
+ else {
13139
+ results.unshift(insertion);
13140
+ }
13141
+ }
13142
+ else {
13143
+ results.push(insertion);
13144
+ }
13145
+ });
13146
+ return results.length === 1 ? null : results;
13147
+ };
13148
+ if (node.type === "ExpressionStatement" &&
13149
+ node.expression.type === "SequenceExpression") {
13150
+ const results = [];
13151
+ node.expression.expressions.forEach((expression) => {
13152
+ results.push({ ...node, expression });
13153
+ });
13154
+ if (!pending) {
13155
+ return results;
13156
+ }
13157
+ return applyPending(results, node);
13158
+ }
13159
+ if (pending) {
13160
+ return applyPending([node], node);
13161
+ }
13162
+ return null;
13163
+ });
13164
+ }
13165
+
13166
+ ;// CONCATENATED MODULE: ./src/visitor.ts
13167
+
13168
+ function visitReferences(state, ast, name, defn, callback) {
13169
+ const checkResults = ([name, results], node) => {
13170
+ if (name && results) {
13171
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
13172
+ if (callback(node, results, false) === false) {
13173
+ return [];
13174
+ }
13175
+ }
13176
+ }
13177
+ else if (defn === false) {
13178
+ if (callback(node, [], results === null) === false) {
13179
+ return [];
13180
+ }
13181
+ }
13182
+ return null;
13183
+ };
13184
+ state.pre = (node) => {
13185
+ switch (node.type) {
13186
+ case "AttributeList":
13187
+ return [];
13188
+ case "UnaryExpression":
13189
+ // a bare symbol isn't a reference
13190
+ if (node.operator === ":")
13191
+ return [];
13192
+ break;
13193
+ case "BinaryExpression":
13194
+ /*
13195
+ * `expr has :symbol` can be treated as a reference
13196
+ * to expr.symbol.
13197
+ */
13198
+ if (node.operator === "has") {
13199
+ if (node.right.type === "UnaryExpression" &&
13200
+ node.right.operator === ":") {
13201
+ if (!name || node.right.argument.name === name) {
13202
+ return checkResults(state.lookup({
13203
+ type: "MemberExpression",
13204
+ object: node.left,
13205
+ property: node.right.argument,
13206
+ computed: false,
13207
+ }), node.right.argument);
13208
+ }
13209
+ }
13210
+ }
13211
+ break;
13212
+ case "CallExpression":
13213
+ // A call expression whose callee is an identifier is looked
13214
+ // up as a non-local. ie even if there's a same named local,
13215
+ // it will be ignored, and the lookup will start as if the
13216
+ // call had been written self.foo() rather than foo().
13217
+ if (node.callee.type === "Identifier") {
13218
+ if (!name || node.callee.name === name) {
13219
+ /* ignore return value */
13220
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
13221
+ }
13222
+ return ["arguments"];
13223
+ }
13224
+ break;
13225
+ case "Identifier":
13226
+ if (!name || node.name === name) {
13227
+ return checkResults(state.lookup(node), node);
13228
+ }
13229
+ break;
13230
+ case "MemberExpression":
13231
+ if (!node.computed && node.property.type === "Identifier") {
13232
+ if (!name || node.property.name === name) {
13233
+ return checkResults(state.lookup(node), node) || ["object"];
13234
+ }
13235
+ return ["object"];
13236
+ }
13237
+ break;
13238
+ case "MethodDefinition": {
13239
+ if (!state.inType) {
13240
+ throw new Error("Method definition outside of type!");
13241
+ }
13242
+ if (node.params) {
13243
+ node.params.forEach((param) => {
13244
+ if (param.type == "BinaryExpression") {
13245
+ state.traverse(param.right);
13246
+ state.inType = true;
13247
+ }
13248
+ });
11612
13249
  }
11613
13250
  return ["returnType"];
11614
13251
  }
@@ -11628,6 +13265,7 @@ function visitReferences(state, ast, name, defn, callback) {
11628
13265
 
11629
13266
 
11630
13267
 
13268
+
11631
13269
  function collectClassInfo(state) {
11632
13270
  const toybox = state.stack[0].decls["Toybox"][0];
11633
13271
  const lang = toybox.decls["Lang"][0];
@@ -11928,7 +13566,50 @@ function getNodeValue(node) {
11928
13566
  }
11929
13567
  return [node, type];
11930
13568
  }
11931
- function optimizeNode(node) {
13569
+ function fullTypeName(state, tsp) {
13570
+ if (typeof tsp.name === "string") {
13571
+ return tsp.name;
13572
+ }
13573
+ const [, results] = state.lookupType(tsp.name);
13574
+ if (results && results.length === 1 && results[0].results.length === 1) {
13575
+ const result = results[0].results[0];
13576
+ if ((0,external_api_cjs_namespaceObject.isStateNode)(result)) {
13577
+ return result.fullName;
13578
+ }
13579
+ }
13580
+ return null;
13581
+ }
13582
+ function isBooleanExpression(state, node) {
13583
+ switch (node.type) {
13584
+ case "Literal":
13585
+ return typeof node.value === "boolean";
13586
+ case "BinaryExpression":
13587
+ switch (node.operator) {
13588
+ case "==":
13589
+ case "!=":
13590
+ case "<=":
13591
+ case ">=":
13592
+ case "<":
13593
+ case ">":
13594
+ return true;
13595
+ case "as":
13596
+ return node.right.ts.length === 1 &&
13597
+ node.right.ts[0].type === "TypeSpecPart" &&
13598
+ node.right.ts[0].name &&
13599
+ fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
13600
+ ? true
13601
+ : false;
13602
+ }
13603
+ return false;
13604
+ case "LogicalExpression":
13605
+ return (isBooleanExpression(state, node.left) &&
13606
+ isBooleanExpression(state, node.right));
13607
+ case "UnaryExpression":
13608
+ return node.operator === "!" && isBooleanExpression(state, node.argument);
13609
+ }
13610
+ return false;
13611
+ }
13612
+ function optimizeNode(state, node) {
11932
13613
  switch (node.type) {
11933
13614
  case "UnaryExpression": {
11934
13615
  const [arg, type] = getNodeValue(node.argument);
@@ -11980,8 +13661,18 @@ function optimizeNode(node) {
11980
13661
  "%": (left, right) => left % right,
11981
13662
  "&": (left, right, type) => type === "Number" ? left & right : null,
11982
13663
  "|": (left, right, type) => type === "Number" ? left | right : null,
13664
+ "^": (left, right, type) => type === "Number" ? left ^ right : null,
11983
13665
  "<<": (left, right, type) => type === "Number" ? left << right : null,
11984
13666
  ">>": (left, right, type) => type === "Number" ? left >> right : null,
13667
+ "==": (left, right) => left == right,
13668
+ "!=": (left, right) => left != right,
13669
+ "<=": (left, right) => left <= right,
13670
+ ">=": (left, right) => left >= right,
13671
+ "<": (left, right) => left < right,
13672
+ ">": (left, right) => left > right,
13673
+ as: null,
13674
+ instanceof: null,
13675
+ has: null,
11985
13676
  };
11986
13677
  const op = operators[node.operator];
11987
13678
  if (op) {
@@ -11989,11 +13680,17 @@ function optimizeNode(node) {
11989
13680
  const [right, right_type] = getNodeValue(node.right);
11990
13681
  if (!left || !right)
11991
13682
  break;
13683
+ let value = null;
11992
13684
  if (left_type != right_type ||
11993
13685
  (left_type != "Number" && left_type != "Long")) {
11994
- break;
13686
+ if (node.operator !== "==" && node.operator !== "!=") {
13687
+ break;
13688
+ }
13689
+ value = operators[node.operator](left.value, right.value);
13690
+ }
13691
+ else {
13692
+ value = op(left.value, right.value, left_type);
11995
13693
  }
11996
- const value = op(left.value, right.value, left_type);
11997
13694
  if (value === null)
11998
13695
  break;
11999
13696
  return {
@@ -12004,15 +13701,47 @@ function optimizeNode(node) {
12004
13701
  }
12005
13702
  break;
12006
13703
  }
13704
+ case "LogicalExpression": {
13705
+ const [left, left_type] = getNodeValue(node.left);
13706
+ if (!left)
13707
+ break;
13708
+ const falsy = left.value === false ||
13709
+ left.value === null ||
13710
+ (left.value === 0 && (left_type === "Number" || left_type === "Long"));
13711
+ if (falsy === (node.operator === "&&")) {
13712
+ return left;
13713
+ }
13714
+ if (left_type !== "Boolean" &&
13715
+ left_type !== "Number" &&
13716
+ left_type !== "Long") {
13717
+ break;
13718
+ }
13719
+ const [right, right_type] = getNodeValue(node.right);
13720
+ if (right && right_type === left_type) {
13721
+ if (left_type === "Boolean" || node.operator === "||") {
13722
+ return right;
13723
+ }
13724
+ if (node.operator !== "&&") {
13725
+ throw new Error(`Unexpected operator "${node.operator}"`);
13726
+ }
13727
+ return { ...node, type: "BinaryExpression", operator: "&" };
13728
+ }
13729
+ if (left_type === "Boolean") {
13730
+ if (isBooleanExpression(state, node.right)) {
13731
+ return node.right;
13732
+ }
13733
+ }
13734
+ break;
13735
+ }
12007
13736
  case "FunctionDeclaration":
12008
- if (node.body && evaluateFunction(node, null) !== false) {
13737
+ if (node.body && evaluateFunction(state, node, null) !== false) {
12009
13738
  node.optimizable = true;
12010
13739
  }
12011
13740
  break;
12012
13741
  }
12013
13742
  return null;
12014
13743
  }
12015
- function evaluateFunction(func, args) {
13744
+ function evaluateFunction(state, func, args) {
12016
13745
  if (!func.body || (args && args.length != func.params.length)) {
12017
13746
  return false;
12018
13747
  }
@@ -12051,7 +13780,7 @@ function evaluateFunction(func, args) {
12051
13780
  }
12052
13781
  // fall through;
12053
13782
  default: {
12054
- const repl = optimizeNode(node);
13783
+ const repl = optimizeNode(state, node);
12055
13784
  if (repl && repl.type === "Literal")
12056
13785
  return repl;
12057
13786
  throw new Error("Didn't optimize");
@@ -12111,10 +13840,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12111
13840
  if (!objects) {
12112
13841
  return false;
12113
13842
  }
12114
- const obj = getLiteralFromDecls(objects);
13843
+ let obj = getLiteralFromDecls(objects);
12115
13844
  if (!obj) {
12116
13845
  return false;
12117
13846
  }
13847
+ while (obj.type === "BinaryExpression") {
13848
+ if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
13849
+ obj = { ...obj, left: obj.left.left };
13850
+ }
13851
+ else {
13852
+ obj = { ...obj, left: { ...obj.left } };
13853
+ break;
13854
+ }
13855
+ }
12118
13856
  inPlaceReplacement(node, obj);
12119
13857
  return true;
12120
13858
  };
@@ -12332,14 +14070,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12332
14070
  return null;
12333
14071
  };
12334
14072
  state.post = (node) => {
12335
- if (topLocals().node === node) {
14073
+ const locals = topLocals();
14074
+ if (locals.node === node) {
12336
14075
  state.localsStack.pop();
12337
14076
  }
12338
- const opt = optimizeNode(node);
14077
+ const opt = optimizeNode(state, node);
12339
14078
  if (opt) {
12340
14079
  return replace(opt, node);
12341
14080
  }
12342
14081
  switch (node.type) {
14082
+ case "BlockStatement":
14083
+ if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
14084
+ node.body.splice(0, 1, ...node.body[0].body);
14085
+ }
14086
+ break;
12343
14087
  case "ConditionalExpression":
12344
14088
  case "IfStatement":
12345
14089
  if (node.test.type === "Literal" &&
@@ -12349,6 +14093,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12349
14093
  return false;
12350
14094
  return replace(rep, rep);
12351
14095
  }
14096
+ else if (node.type === "IfStatement" &&
14097
+ node.alternate &&
14098
+ node.alternate.type === "BlockStatement" &&
14099
+ !node.alternate.body.length) {
14100
+ delete node.alternate;
14101
+ }
12352
14102
  break;
12353
14103
  case "WhileStatement":
12354
14104
  if (node.test.type === "Literal" && node.test.value === false) {
@@ -12376,6 +14126,48 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12376
14126
  return { type: "Literal", value: null, raw: "null" };
12377
14127
  }
12378
14128
  break;
14129
+ case "VariableDeclaration": {
14130
+ const locals = topLocals();
14131
+ if (locals.map &&
14132
+ locals.node &&
14133
+ locals.node.type === "BlockStatement") {
14134
+ let results;
14135
+ const declarations = node.declarations;
14136
+ let i = 0;
14137
+ let j = 0;
14138
+ while (i < node.declarations.length) {
14139
+ const decl = declarations[i++];
14140
+ if (decl.init && decl.init.type === "CallExpression") {
14141
+ const inlined = optimizeCall(state, decl.init, decl);
14142
+ if (!inlined)
14143
+ continue;
14144
+ if (inlined.type != "BlockStatement") {
14145
+ throw new Error("Unexpected inlined result");
14146
+ }
14147
+ if (!results) {
14148
+ results = [];
14149
+ }
14150
+ delete decl.init;
14151
+ results.push(withLoc({
14152
+ ...node,
14153
+ declarations: declarations.slice(j, i),
14154
+ }, j ? declarations[j] : null, decl.id));
14155
+ results.push(inlined);
14156
+ j = i;
14157
+ }
14158
+ }
14159
+ if (results) {
14160
+ if (j < i) {
14161
+ results.push({
14162
+ ...node,
14163
+ declarations: declarations.slice(j, i),
14164
+ });
14165
+ }
14166
+ return results;
14167
+ }
14168
+ }
14169
+ break;
14170
+ }
12379
14171
  case "ExpressionStatement":
12380
14172
  if (node.expression.type === "CallExpression") {
12381
14173
  return replace(optimizeCall(state, node.expression, node), node.expression);
@@ -12420,6 +14212,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12420
14212
  });
12421
14213
  delete state.pre;
12422
14214
  delete state.post;
14215
+ state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
12423
14216
  const cleanup = (node) => {
12424
14217
  switch (node.type) {
12425
14218
  case "ThisExpression":
@@ -12545,7 +14338,7 @@ function optimizeCall(state, node, context) {
12545
14338
  callee.optimizable &&
12546
14339
  !callee.hasOverride &&
12547
14340
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12548
- const ret = evaluateFunction(callee, node.arguments);
14341
+ const ret = evaluateFunction(state, callee, node.arguments);
12549
14342
  if (ret) {
12550
14343
  return ret;
12551
14344
  }
@@ -12606,7 +14399,7 @@ function pragmaChecker(ast, diagnostics) {
12606
14399
  if (quote == '"') {
12607
14400
  return haystack.includes(needle);
12608
14401
  }
12609
- const re = new RegExp(needle);
14402
+ const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
12610
14403
  return re.test(haystack);
12611
14404
  };
12612
14405
  next();
@@ -12616,8 +14409,10 @@ function pragmaChecker(ast, diagnostics) {
12616
14409
  if (node.start && node.start >= (comment.end || Infinity)) {
12617
14410
  const { kind, quote, needle } = matchers.shift();
12618
14411
  if (kind === "match") {
12619
- if (!matcher(quote, needle, (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " "))) {
12620
- throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
14412
+ const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
14413
+ if (!matcher(quote, needle, haystack)) {
14414
+ matcher(quote, needle, haystack);
14415
+ throw new Error(`Didn't find '${needle}' in '${haystack}' at ${comment.loc.source}:${comment.loc.start.line}`);
12621
14416
  }
12622
14417
  }
12623
14418
  else if (kind === "expect") {
@@ -13058,6 +14853,7 @@ const configOptionsToCheck = [
13058
14853
  "ignoredAnnotations",
13059
14854
  "ignoredSourcePaths",
13060
14855
  "checkInvalidSymbols",
14856
+ "sizeBasedPRE",
13061
14857
  ];
13062
14858
  /**
13063
14859
  * @param {BuildConfig} config
@@ -13109,7 +14905,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
13109
14905
  // the oldest optimized file, we don't need to regenerate
13110
14906
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
13111
14907
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
13112
- if (source_time < opt_time && 1655916886685 < opt_time) {
14908
+ if (source_time < opt_time && 1656718632916 < opt_time) {
13113
14909
  return { hasTests, diagnostics: prevDiagnostics };
13114
14910
  }
13115
14911
  }