@markw65/monkeyc-optimizer 1.0.27 → 1.0.30

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
 
@@ -11187,6 +11429,7 @@ function processInlineBody(state, func, call, root, params) {
11187
11429
  const post = state.post;
11188
11430
  state.inlining = true;
11189
11431
  let insertedVariableDecls = null;
11432
+ const replacements = new Set();
11190
11433
  try {
11191
11434
  state.pre = (node) => {
11192
11435
  if (failed)
@@ -11194,7 +11437,7 @@ function processInlineBody(state, func, call, root, params) {
11194
11437
  node.start = call.start;
11195
11438
  node.end = call.end;
11196
11439
  node.loc = call.loc;
11197
- if (node === insertedVariableDecls)
11440
+ if (replacements.has(node))
11198
11441
  return false;
11199
11442
  const result = pre(node, state);
11200
11443
  if (!insertedVariableDecls && node.type === "BlockStatement") {
@@ -11222,6 +11465,7 @@ function processInlineBody(state, func, call, root, params) {
11222
11465
  kind: "var",
11223
11466
  };
11224
11467
  node.body.unshift(insertedVariableDecls);
11468
+ replacements.add(insertedVariableDecls);
11225
11469
  }
11226
11470
  return result;
11227
11471
  };
@@ -11236,7 +11480,9 @@ function processInlineBody(state, func, call, root, params) {
11236
11480
  if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
11237
11481
  const ix = params[node.name];
11238
11482
  if (ix >= 0) {
11239
- replacement = call.arguments[ix];
11483
+ replacement = { ...call.arguments[ix] };
11484
+ replacements.add(replacement);
11485
+ return replacement;
11240
11486
  }
11241
11487
  break;
11242
11488
  }
@@ -11273,6 +11519,10 @@ function processInlineBody(state, func, call, root, params) {
11273
11519
  }
11274
11520
  }
11275
11521
  function unused(expression, top) {
11522
+ const estmt = (expression) => withLoc({
11523
+ type: "ExpressionStatement",
11524
+ expression,
11525
+ }, expression);
11276
11526
  switch (expression.type) {
11277
11527
  case "Literal":
11278
11528
  return [];
@@ -11284,9 +11534,50 @@ function unused(expression, top) {
11284
11534
  if (expression.operator === "as") {
11285
11535
  return unused(expression.left);
11286
11536
  }
11287
- // fall through
11288
- case "LogicalExpression":
11289
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
+ }
11290
11581
  case "UnaryExpression":
11291
11582
  return unused(expression.argument);
11292
11583
  case "MemberExpression":
@@ -11301,17 +11592,7 @@ function unused(expression, top) {
11301
11592
  .map((p) => unused(p.key).concat(unused(p.value)))
11302
11593
  .flat(1);
11303
11594
  }
11304
- return top
11305
- ? null
11306
- : [
11307
- {
11308
- type: "ExpressionStatement",
11309
- expression,
11310
- start: expression.start,
11311
- end: expression.end,
11312
- loc: expression.loc,
11313
- },
11314
- ];
11595
+ return top ? null : [estmt(expression)];
11315
11596
  }
11316
11597
  function diagnostic(state, loc, message, type = "INFO") {
11317
11598
  if (!loc || !loc.source)
@@ -11344,6 +11625,10 @@ function inlineWithArgs(state, func, call, context) {
11344
11625
  if (!func.node || !func.node.body) {
11345
11626
  return null;
11346
11627
  }
11628
+ const lastStmt = (block) => {
11629
+ const last = block.body.slice(-1)[0];
11630
+ return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
11631
+ };
11347
11632
  let retStmtCount = 0;
11348
11633
  if (context.type === "ReturnStatement") {
11349
11634
  const last = func.node.body.body.slice(-1)[0];
@@ -11359,15 +11644,19 @@ function inlineWithArgs(state, func, call, context) {
11359
11644
  if (retStmtCount > 1) {
11360
11645
  inlineDiagnostic(state, func, call, "Function had more than one return statement");
11361
11646
  }
11362
- else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
11647
+ else if ((context.type === "AssignmentExpression" ||
11648
+ context.type === "VariableDeclarator") &&
11649
+ retStmtCount !== 1) {
11363
11650
  inlineDiagnostic(state, func, call, "Function did not have a return statement");
11364
11651
  return null;
11365
11652
  }
11366
11653
  if (retStmtCount === 1) {
11367
- const last = func.node.body.body.slice(-1)[0];
11654
+ const [last] = lastStmt(func.node.body);
11368
11655
  if (!last ||
11369
11656
  last.type !== "ReturnStatement" ||
11370
- (context.type === "AssignmentExpression" && !last.argument)) {
11657
+ ((context.type === "AssignmentExpression" ||
11658
+ context.type === "VariableDeclarator") &&
11659
+ !last.argument)) {
11371
11660
  inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
11372
11661
  return null;
11373
11662
  }
@@ -11387,23 +11676,39 @@ function inlineWithArgs(state, func, call, context) {
11387
11676
  }
11388
11677
  diagnostic(state, call.loc, null);
11389
11678
  if (context.type !== "ReturnStatement" && retStmtCount) {
11390
- const last = body.body[body.body.length - 1];
11679
+ const [last, block] = lastStmt(body);
11391
11680
  if (last.type != "ReturnStatement") {
11392
11681
  throw new Error("ReturnStatement got lost!");
11393
11682
  }
11394
- if (context.type === "AssignmentExpression") {
11395
- context.right = last.argument;
11396
- body.body[body.body.length - 1] = {
11397
- type: "ExpressionStatement",
11398
- expression: context,
11399
- };
11400
- }
11401
- else if (last.argument) {
11402
- const side_exprs = unused(last.argument);
11403
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
11683
+ if (last.argument) {
11684
+ if (context.type === "AssignmentExpression") {
11685
+ context.right = last.argument;
11686
+ block.body[block.body.length - 1] = {
11687
+ type: "ExpressionStatement",
11688
+ expression: context,
11689
+ };
11690
+ }
11691
+ else if (context.type === "VariableDeclarator") {
11692
+ const { id, init: _init, kind: _kind, ...rest } = context;
11693
+ block.body[block.body.length - 1] = {
11694
+ ...rest,
11695
+ type: "ExpressionStatement",
11696
+ expression: {
11697
+ ...rest,
11698
+ type: "AssignmentExpression",
11699
+ operator: "=",
11700
+ left: id.type === "Identifier" ? id : id.left,
11701
+ right: last.argument,
11702
+ },
11703
+ };
11704
+ }
11705
+ else {
11706
+ const side_exprs = unused(last.argument);
11707
+ block.body.splice(block.body.length - 1, 1, ...side_exprs);
11708
+ }
11404
11709
  }
11405
11710
  else {
11406
- --body.body.length;
11711
+ --block.body.length;
11407
11712
  }
11408
11713
  }
11409
11714
  return body;
@@ -11522,87 +11827,1461 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11522
11827
  return null;
11523
11828
  }
11524
11829
 
11525
- ;// CONCATENATED MODULE: ./src/visitor.ts
11830
+ ;// CONCATENATED MODULE: ./src/control-flow.ts
11526
11831
 
11527
- function visitReferences(state, ast, name, defn, callback) {
11528
- const checkResults = ([name, results], node) => {
11529
- if (name && results) {
11530
- if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
11531
- if (callback(node, results, false) === false) {
11532
- return [];
11832
+
11833
+ const Terminals = {
11834
+ BreakStatement: "break",
11835
+ ContinueStatement: "continue",
11836
+ ReturnStatement: null,
11837
+ ThrowStatement: "throw",
11838
+ };
11839
+ class LocalState {
11840
+ constructor(func) {
11841
+ this.stack = [];
11842
+ this.info = new Map();
11843
+ this.curBlock = {};
11844
+ this.unreachable = false;
11845
+ this.push(func);
11846
+ }
11847
+ push(node) {
11848
+ const top = { node };
11849
+ this.stack.push(top);
11850
+ return top;
11851
+ }
11852
+ pop() {
11853
+ return this.stack.pop();
11854
+ }
11855
+ top(depth) {
11856
+ return this.stack[this.stack.length - (depth || 1)];
11857
+ }
11858
+ addEdge(from, to) {
11859
+ if (!from.succs) {
11860
+ from.succs = [to];
11861
+ }
11862
+ else {
11863
+ (0,external_util_cjs_namespaceObject.pushUnique)(from.succs, to);
11864
+ }
11865
+ if (!to.preds) {
11866
+ to.preds = [from];
11867
+ }
11868
+ else {
11869
+ (0,external_util_cjs_namespaceObject.pushUnique)(to.preds, from);
11870
+ }
11871
+ }
11872
+ newBlock(block) {
11873
+ if (!block)
11874
+ block = {};
11875
+ if (!this.unreachable) {
11876
+ this.addEdge(this.curBlock, block);
11877
+ }
11878
+ this.unreachable = false;
11879
+ for (let i = this.stack.length; i--;) {
11880
+ const si = this.stack[i];
11881
+ if (si.throw) {
11882
+ block.exsucc = si.throw;
11883
+ if (!si.throw.expreds) {
11884
+ si.throw.expreds = [block];
11533
11885
  }
11886
+ else {
11887
+ si.throw.expreds.push(block);
11888
+ }
11889
+ break;
11534
11890
  }
11535
11891
  }
11536
- else if (defn === false) {
11537
- if (callback(node, [], results === null) === false) {
11538
- return [];
11892
+ return (this.curBlock = block);
11893
+ }
11894
+ terminal(type) {
11895
+ const re = Terminals[type];
11896
+ if (re) {
11897
+ for (let i = this.stack.length; i--;) {
11898
+ const target = this.stack[i][re];
11899
+ if (target) {
11900
+ this.addEdge(this.curBlock, target);
11901
+ break;
11902
+ }
11539
11903
  }
11540
11904
  }
11541
- return null;
11542
- };
11543
- state.pre = (node) => {
11544
- switch (node.type) {
11545
- case "AttributeList":
11905
+ this.unreachable = true;
11906
+ }
11907
+ }
11908
+ function buildReducedGraph(state, func, notice) {
11909
+ const { stack, pre, post } = state;
11910
+ try {
11911
+ const localState = new LocalState(func.node);
11912
+ const ret = localState.curBlock;
11913
+ state.stack = func.stack;
11914
+ const stmtStack = [func.node];
11915
+ let tryActive = 0;
11916
+ state.pre = (node) => {
11917
+ if (state.inType || localState.unreachable) {
11546
11918
  return [];
11547
- case "UnaryExpression":
11548
- // a bare symbol isn't a reference
11549
- if (node.operator === ":")
11919
+ }
11920
+ if (!localState.curBlock.node &&
11921
+ (isStatement(node) || isExpression(node))) {
11922
+ localState.curBlock.node = node;
11923
+ }
11924
+ if (isStatement(node)) {
11925
+ stmtStack.push(node);
11926
+ }
11927
+ switch (node.type) {
11928
+ case "AttributeList":
11550
11929
  return [];
11551
- break;
11552
- case "BinaryExpression":
11553
- /*
11554
- * `expr has :symbol` can be treated as a reference
11555
- * to expr.symbol.
11556
- */
11557
- if (node.operator === "has") {
11558
- if (node.right.type === "UnaryExpression" &&
11559
- node.right.operator === ":") {
11560
- if (!name || node.right.argument.name === name) {
11561
- return checkResults(state.lookup({
11562
- type: "MemberExpression",
11563
- object: node.left,
11564
- property: node.right.argument,
11565
- computed: false,
11566
- }), node.right.argument);
11930
+ case "SwitchStatement": {
11931
+ const top = localState.push(node);
11932
+ top.break = {};
11933
+ state.traverse(node.discriminant);
11934
+ const testBlocks = [];
11935
+ let defaultSeen = false;
11936
+ node.cases.forEach((sc, i) => {
11937
+ if (sc.test) {
11938
+ state.traverse(sc.test);
11939
+ testBlocks[i] = localState.curBlock;
11940
+ localState.newBlock();
11941
+ }
11942
+ else {
11943
+ defaultSeen = true;
11567
11944
  }
11945
+ });
11946
+ const endOfTests = localState.curBlock;
11947
+ if (!defaultSeen) {
11948
+ localState.addEdge(endOfTests, top.break);
11568
11949
  }
11950
+ localState.unreachable = true;
11951
+ node.cases.forEach((sc, i) => {
11952
+ localState.newBlock();
11953
+ localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
11954
+ sc.consequent.every((s) => {
11955
+ state.traverse(s);
11956
+ return !localState.unreachable;
11957
+ });
11958
+ });
11959
+ localState.newBlock(top.break);
11960
+ localState.unreachable = !top.break.preds;
11961
+ return [];
11569
11962
  }
11570
- break;
11571
- case "CallExpression":
11572
- // A call expression whose callee is an identifier is looked
11573
- // up as a non-local. ie even if there's a same named local,
11574
- // it will be ignored, and the lookup will start as if the
11575
- // call had been written self.foo() rather than foo().
11576
- if (node.callee.type === "Identifier") {
11577
- if (!name || node.callee.name === name) {
11578
- /* ignore return value */
11579
- checkResults(state.lookupNonlocal(node.callee), node.callee);
11963
+ case "DoWhileStatement":
11964
+ case "WhileStatement": {
11965
+ localState.push(node);
11966
+ const top = localState.top();
11967
+ top.break = {};
11968
+ top.continue = {};
11969
+ let head;
11970
+ if (node.type === "WhileStatement") {
11971
+ head = localState.newBlock(top.continue);
11972
+ state.traverse(node.test);
11973
+ localState.addEdge(localState.curBlock, top.break);
11974
+ localState.newBlock();
11580
11975
  }
11581
- return ["arguments"];
11976
+ else {
11977
+ head = localState.newBlock();
11978
+ }
11979
+ state.traverse(node.body);
11980
+ if (node.type === "DoWhileStatement") {
11981
+ localState.newBlock(top.continue);
11982
+ state.traverse(node.test);
11983
+ localState.addEdge(localState.curBlock, top.break);
11984
+ }
11985
+ localState.addEdge(localState.curBlock, head);
11986
+ localState.curBlock = top.break;
11987
+ return [];
11582
11988
  }
11583
- break;
11584
- case "Identifier":
11585
- if (!name || node.name === name) {
11586
- return checkResults(state.lookup(node), node);
11989
+ case "TryStatement": {
11990
+ const top = localState.push(node);
11991
+ const catches = (top.throw = {});
11992
+ // This edge shouldn't exist, but we can trigger
11993
+ // (incorrect) "variable may not be initialized" errors
11994
+ // in the monkey c compiler without it.
11995
+ // https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
11996
+ localState.addEdge(localState.curBlock, top.throw);
11997
+ localState.newBlock();
11998
+ tryActive++;
11999
+ state.traverse(node.block);
12000
+ tryActive--;
12001
+ delete top.throw;
12002
+ top.posttry = {};
12003
+ const tryFallsThrough = !localState.unreachable;
12004
+ if (node.finalizer) {
12005
+ tryActive++;
12006
+ top.throw = top.finally = {};
12007
+ // curBlock branches to finally, no matter how it exits.
12008
+ localState.addEdge(localState.curBlock, top.finally);
12009
+ }
12010
+ else {
12011
+ if (!localState.unreachable) {
12012
+ localState.addEdge(localState.curBlock, top.posttry);
12013
+ }
12014
+ }
12015
+ localState.unreachable = true;
12016
+ localState.newBlock(catches);
12017
+ if (node.handler) {
12018
+ state.traverse(node.handler);
12019
+ if (top.throw) {
12020
+ tryActive--;
12021
+ delete top.throw;
12022
+ }
12023
+ // Each "catch (ex instanceof Foo)" chains to the next,
12024
+ // but "catch (ex)" terminates the list. If the end
12025
+ // of the chain has a predecessor, its possible that
12026
+ // none of the conditions matched, so the exception
12027
+ // will propagate from there.
12028
+ if (localState.curBlock.preds) {
12029
+ localState.terminal("ThrowStatement");
12030
+ }
12031
+ }
12032
+ if (top.throw) {
12033
+ tryActive--;
12034
+ delete top.throw;
12035
+ }
12036
+ if (node.finalizer) {
12037
+ localState.unreachable = true;
12038
+ localState.newBlock(top.finally);
12039
+ delete top.finally;
12040
+ state.traverse(node.finalizer);
12041
+ if (tryFallsThrough && !localState.unreachable) {
12042
+ localState.addEdge(localState.curBlock, top.posttry);
12043
+ }
12044
+ localState.terminal("ThrowStatement");
12045
+ }
12046
+ localState.unreachable = true;
12047
+ localState.newBlock(top.posttry);
12048
+ return [];
11587
12049
  }
11588
- break;
11589
- case "MemberExpression":
11590
- if (!node.computed && node.property.type === "Identifier") {
11591
- if (!name || node.property.name === name) {
11592
- return checkResults(state.lookup(node), node) || ["object"];
12050
+ case "CatchClause": {
12051
+ const top = localState.top();
12052
+ if (!localState.curBlock.preds && !localState.curBlock.expreds) {
12053
+ return [];
11593
12054
  }
11594
- return ["object"];
12055
+ const next = {};
12056
+ if (node.param && node.param.type === "BinaryExpression") {
12057
+ state.traverse(node.param);
12058
+ localState.addEdge(localState.curBlock, next);
12059
+ localState.newBlock();
12060
+ }
12061
+ state.traverse(node.body);
12062
+ if (top.finally) {
12063
+ // this edge exists even if this point is unreachable
12064
+ localState.addEdge(localState.curBlock, top.finally);
12065
+ }
12066
+ if (!localState.unreachable) {
12067
+ if (!top.posttry)
12068
+ top.posttry = {};
12069
+ localState.addEdge(localState.curBlock, top.posttry);
12070
+ }
12071
+ localState.unreachable = true;
12072
+ localState.newBlock(next);
12073
+ return [];
11595
12074
  }
11596
- break;
11597
- case "MethodDefinition": {
11598
- if (!state.inType) {
11599
- throw new Error("Method definition outside of type!");
12075
+ case "ForStatement": {
12076
+ const top = localState.push(node);
12077
+ if (node.init)
12078
+ state.traverse(node.init);
12079
+ const head = localState.newBlock();
12080
+ top.break = {};
12081
+ top.continue = {};
12082
+ if (node.test) {
12083
+ state.traverse(node.test);
12084
+ localState.addEdge(localState.curBlock, top.break);
12085
+ localState.newBlock();
12086
+ }
12087
+ state.traverse(node.body);
12088
+ localState.newBlock(top.continue);
12089
+ if (node.update) {
12090
+ state.traverse(node.update);
12091
+ }
12092
+ if (!localState.unreachable) {
12093
+ localState.addEdge(localState.curBlock, head);
12094
+ }
12095
+ // there is no fall through from the end of the loop
12096
+ // to the next block. The only way there is via break
12097
+ // or the test failing.
12098
+ localState.unreachable = true;
12099
+ localState.newBlock(top.break);
12100
+ if (!top.break.preds) {
12101
+ localState.unreachable = true;
12102
+ }
12103
+ return [];
11600
12104
  }
11601
- if (node.params) {
11602
- node.params.forEach((param) => {
11603
- if (param.type == "BinaryExpression") {
11604
- state.traverse(param.right);
11605
- state.inType = true;
12105
+ case "IfStatement":
12106
+ case "ConditionalExpression": {
12107
+ state.traverse(node.test);
12108
+ const alternate = {};
12109
+ localState.addEdge(localState.curBlock, alternate);
12110
+ localState.newBlock();
12111
+ state.traverse(node.consequent);
12112
+ const consequent = localState.unreachable
12113
+ ? null
12114
+ : localState.curBlock;
12115
+ localState.unreachable = true;
12116
+ localState.newBlock(alternate);
12117
+ if (node.alternate) {
12118
+ state.traverse(node.alternate);
12119
+ if (!localState.unreachable) {
12120
+ localState.newBlock();
12121
+ }
12122
+ }
12123
+ if (consequent) {
12124
+ if (localState.unreachable) {
12125
+ localState.newBlock();
12126
+ }
12127
+ localState.addEdge(consequent, localState.curBlock);
12128
+ }
12129
+ return [];
12130
+ }
12131
+ case "LogicalExpression": {
12132
+ state.traverse(node.left);
12133
+ if (localState.unreachable)
12134
+ break;
12135
+ const mid = localState.curBlock;
12136
+ localState.newBlock();
12137
+ state.traverse(node.right);
12138
+ localState.newBlock();
12139
+ localState.addEdge(mid, localState.curBlock);
12140
+ return [];
12141
+ }
12142
+ case "VariableDeclarator":
12143
+ return ["init"];
12144
+ case "MemberExpression":
12145
+ if (!node.computed) {
12146
+ return ["object"];
12147
+ }
12148
+ break;
12149
+ case "UnaryExpression":
12150
+ if (node.operator === ":") {
12151
+ return [];
12152
+ }
12153
+ break;
12154
+ case "UpdateExpression":
12155
+ // We don't want to traverse the argument, since then it would
12156
+ // look like a ref, rather than a def. But if its a
12157
+ // MemberExpression, we *do* want to traverse the subexpressions
12158
+ // as potential refs.
12159
+ if (node.argument.type === "MemberExpression") {
12160
+ state.traverse(node.argument.object);
12161
+ if (node.argument.computed) {
12162
+ state.traverse(node.argument.property);
12163
+ }
12164
+ }
12165
+ return [];
12166
+ case "AssignmentExpression":
12167
+ if (node.left.type === "MemberExpression") {
12168
+ state.traverse(node.left.object);
12169
+ if (node.left.computed) {
12170
+ state.traverse(node.left.property);
12171
+ }
12172
+ }
12173
+ return ["right"];
12174
+ case "ThrowStatement":
12175
+ case "ReturnStatement":
12176
+ if (node.argument) {
12177
+ state.traverse(node.argument);
12178
+ }
12179
+ // fall through
12180
+ case "BreakStatement":
12181
+ case "ContinueStatement":
12182
+ localState.terminal(node.type);
12183
+ return [];
12184
+ }
12185
+ return null;
12186
+ };
12187
+ const addEvent = (block, event) => {
12188
+ if (!block.events) {
12189
+ block.events = [event];
12190
+ }
12191
+ else {
12192
+ block.events.push(event);
12193
+ }
12194
+ };
12195
+ state.post = (node) => {
12196
+ const curStmt = stmtStack[stmtStack.length - 1];
12197
+ if (!state.inType) {
12198
+ const throws = tryActive > 0 && mayThrow(node);
12199
+ const event = notice(node, curStmt, throws);
12200
+ if (throws) {
12201
+ if (!event) {
12202
+ throw new Error("mayThrow expression in try/catch must generate an event");
12203
+ }
12204
+ }
12205
+ else if (event) {
12206
+ event.mayThrow = false;
12207
+ }
12208
+ if (event) {
12209
+ if (event.mayThrow) {
12210
+ for (let i = localState.stack.length; i--;) {
12211
+ const target = localState.stack[i].throw;
12212
+ if (target) {
12213
+ if (localState.curBlock.exsucc) {
12214
+ if (localState.curBlock.exsucc !== target) {
12215
+ throw new Error(`Block has multiple throw targets`);
12216
+ }
12217
+ }
12218
+ else {
12219
+ localState.curBlock.exsucc = target;
12220
+ if (!target.expreds) {
12221
+ target.expreds = [localState.curBlock];
12222
+ }
12223
+ else {
12224
+ target.expreds.push(localState.curBlock);
12225
+ }
12226
+ }
12227
+ break;
12228
+ }
12229
+ }
12230
+ }
12231
+ addEvent(localState.curBlock, event);
12232
+ }
12233
+ }
12234
+ if (curStmt === node) {
12235
+ stmtStack.pop();
12236
+ }
12237
+ if (localState.top().node === node) {
12238
+ localState.pop();
12239
+ }
12240
+ return null;
12241
+ };
12242
+ state.traverse(func.node);
12243
+ return cleanCfg(ret);
12244
+ }
12245
+ finally {
12246
+ state.pre = pre;
12247
+ state.post = post;
12248
+ state.stack = stack;
12249
+ }
12250
+ }
12251
+ function cleanCfg(head) {
12252
+ preOrderTraverse(head, (cur) => {
12253
+ if (cur.succs && cur.succs.length === 1) {
12254
+ const succ = cur.succs[0];
12255
+ if (succ !== head &&
12256
+ succ.preds.length === 1 &&
12257
+ (!cur.exsucc || cur.exsucc === succ.exsucc) &&
12258
+ (!succ.succs ||
12259
+ succ.succs.length === 1 ||
12260
+ (cur.preds && cur.preds.length === 1))) {
12261
+ if (cur.events) {
12262
+ if (succ.events) {
12263
+ cur.events.push(...succ.events);
12264
+ }
12265
+ }
12266
+ else if (succ.events) {
12267
+ cur.events = succ.events;
12268
+ }
12269
+ if (succ.exsucc) {
12270
+ const preds = succ.exsucc.expreds;
12271
+ for (let i = preds.length; i--;) {
12272
+ if (preds[i] === succ) {
12273
+ // If cur has an exsucc, we already
12274
+ // checked that its the same as succ's,
12275
+ // so we can just delete the edge.
12276
+ // Otherwise, we need to point it at cur.
12277
+ if (cur.exsucc) {
12278
+ preds.splice(i, 1);
12279
+ }
12280
+ else {
12281
+ preds[i] = cur;
12282
+ }
12283
+ }
12284
+ }
12285
+ }
12286
+ cur.exsucc = succ.exsucc;
12287
+ cur.succs = succ.succs;
12288
+ if (cur.succs) {
12289
+ cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
12290
+ if (p === succ) {
12291
+ arr[i] = cur;
12292
+ }
12293
+ }));
12294
+ }
12295
+ if (!cur.node)
12296
+ cur.node = succ.node;
12297
+ }
12298
+ }
12299
+ });
12300
+ return head;
12301
+ }
12302
+ function postOrderTraverse(head, visitor) {
12303
+ const visited = new Set();
12304
+ const helper = (cur) => {
12305
+ if (visited.has(cur))
12306
+ return;
12307
+ visited.add(cur);
12308
+ if (cur.succs) {
12309
+ cur.succs.forEach((block) => helper(block));
12310
+ }
12311
+ if (cur.exsucc)
12312
+ helper(cur.exsucc);
12313
+ visitor(cur);
12314
+ };
12315
+ helper(head);
12316
+ }
12317
+ function preOrderTraverse(head, visitor) {
12318
+ const visited = new Set();
12319
+ const helper = (cur) => {
12320
+ if (visited.has(cur))
12321
+ return;
12322
+ visited.add(cur);
12323
+ visitor(cur);
12324
+ if (cur.succs) {
12325
+ cur.succs.forEach((block) => helper(block));
12326
+ }
12327
+ if (cur.exsucc)
12328
+ helper(cur.exsucc);
12329
+ };
12330
+ helper(head);
12331
+ }
12332
+ function getPostOrder(head) {
12333
+ const blocks = [];
12334
+ postOrderTraverse(head, (block) => blocks.push(block));
12335
+ return blocks;
12336
+ }
12337
+ function getPreOrder(head) {
12338
+ const blocks = [];
12339
+ postOrderTraverse(head, (block) => blocks.push(block));
12340
+ return blocks;
12341
+ }
12342
+
12343
+ // EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
12344
+ var priorityqueuejs = __webpack_require__(2789);
12345
+ ;// CONCATENATED MODULE: ./src/pre.ts
12346
+
12347
+
12348
+
12349
+
12350
+ /**
12351
+ * This implements a pseudo Partial Redundancy Elimination
12352
+ * pass. It isn't quite like traditional PRE because we're
12353
+ * aiming to minimize size, not dynamic instructions. So
12354
+ * for us, its worthwhile to take something like:
12355
+ *
12356
+ * switch (x) {
12357
+ * case 1: foo(A.B); break;
12358
+ * case 2: foo(C); break;
12359
+ * case 3: bar(A.B); break;
12360
+ * }
12361
+ *
12362
+ * and rewrite it as
12363
+ *
12364
+ * var tmp = A.B;
12365
+ * switch (x) {
12366
+ * case 1: foo(tmp); break;
12367
+ * case 2: foo(C); break;
12368
+ * case 3: bar(tmp); break;
12369
+ * }
12370
+ *
12371
+ * because even though A.B wasn't used on all paths where we
12372
+ * inserted the temporary, we still reduced the code size.
12373
+ */
12374
+ const logging = false;
12375
+ function declFullName(decl) {
12376
+ switch (decl.type) {
12377
+ case "Literal":
12378
+ return decl.raw || decl.value?.toString() || "null";
12379
+ case "VariableDeclarator":
12380
+ return decl.fullName;
12381
+ default:
12382
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12383
+ }
12384
+ }
12385
+ function declName(decl) {
12386
+ switch (decl.type) {
12387
+ case "Literal":
12388
+ return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
12389
+ case "VariableDeclarator":
12390
+ return decl.name;
12391
+ default:
12392
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12393
+ }
12394
+ }
12395
+ function logAntState(s, decl) {
12396
+ const defs = Array.from(s.ant).reduce((defs, event) => {
12397
+ if (event.type === "def" || event.type === "mod")
12398
+ defs++;
12399
+ return defs;
12400
+ }, 0);
12401
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
12402
+ console.log(` - members: ${Array.from(s.members)
12403
+ .map(([block, live]) => block.order + (live ? "t" : "f"))
12404
+ .join(", ")}`);
12405
+ }
12406
+ function logAntDecls(antDecls) {
12407
+ antDecls.forEach(logAntState);
12408
+ }
12409
+ function sizeBasedPRE(state, func) {
12410
+ if (!func.node.body)
12411
+ return;
12412
+ if (!state.config ||
12413
+ !state.config.sizeBasedPRE ||
12414
+ (typeof state.config.sizeBasedPRE === "string" &&
12415
+ state.config.sizeBasedPRE !== func.fullName)) {
12416
+ return;
12417
+ }
12418
+ const { graph: head, identifiers } = buildPREGraph(state, func);
12419
+ const candidates = computeAttributes(head);
12420
+ if (candidates) {
12421
+ if (logging) {
12422
+ console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
12423
+ logAntDecls(candidates);
12424
+ }
12425
+ const nodeMap = new Map();
12426
+ const declMap = new Map();
12427
+ const variableDecl = withLoc({
12428
+ type: "VariableDeclaration",
12429
+ declarations: [],
12430
+ kind: "var",
12431
+ }, func.node.body);
12432
+ variableDecl.end = variableDecl.start;
12433
+ variableDecl.loc.end = variableDecl.loc.start;
12434
+ candidates.forEach((s, decl) => {
12435
+ let name;
12436
+ let i = 0;
12437
+ do {
12438
+ name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
12439
+ if (!identifiers.has(name))
12440
+ break;
12441
+ i++;
12442
+ } while (true);
12443
+ declMap.set(decl, name);
12444
+ variableDecl.declarations.push(withLoc({
12445
+ type: "VariableDeclarator",
12446
+ id: withLoc({ type: "Identifier", name }, variableDecl),
12447
+ kind: "var",
12448
+ }, variableDecl));
12449
+ s.ant.forEach((event) => {
12450
+ const events = nodeMap.get(event.node);
12451
+ if (!events) {
12452
+ nodeMap.set(event.node, [event]);
12453
+ }
12454
+ else {
12455
+ events.push(event);
12456
+ }
12457
+ });
12458
+ });
12459
+ applyReplacements(func.node, nodeMap, declMap);
12460
+ func.node.body.body.unshift(variableDecl);
12461
+ }
12462
+ }
12463
+ function unhandledExpression(node) {
12464
+ throw new Error(`Unhandled expression type: ${node.type}`);
12465
+ }
12466
+ function buildPREGraph(state, func) {
12467
+ const findDecl = (node) => {
12468
+ if (node.type === "Identifier" ||
12469
+ (node.type === "MemberExpression" && !node.computed)) {
12470
+ const [, results] = state.lookup(node);
12471
+ if (results &&
12472
+ results.length === 1 &&
12473
+ results[0].parent?.type != "BlockStatement" &&
12474
+ results[0].results.length === 1 &&
12475
+ results[0].results[0].type === "VariableDeclarator") {
12476
+ return results[0].results[0];
12477
+ }
12478
+ }
12479
+ return null;
12480
+ };
12481
+ const literals = new Map();
12482
+ const identifiers = new Set();
12483
+ const liveDefs = new Map();
12484
+ const liveStmts = new Map();
12485
+ const liveDef = (def, stmt) => {
12486
+ let curNodes = liveDefs.get(def);
12487
+ if (!curNodes) {
12488
+ liveDefs.set(def, (curNodes = new Set()));
12489
+ }
12490
+ curNodes.add(stmt);
12491
+ let defs = liveStmts.get(stmt);
12492
+ if (!defs) {
12493
+ liveStmts.set(stmt, (defs = new Map()));
12494
+ }
12495
+ defs.set(def, (defs.get(def) || 0) + 1);
12496
+ };
12497
+ return {
12498
+ identifiers,
12499
+ graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
12500
+ const defs = liveStmts.get(node);
12501
+ if (defs) {
12502
+ liveStmts.delete(node);
12503
+ defs.forEach((count, def) => {
12504
+ if (count > 1) {
12505
+ defs.set(def, count--);
12506
+ return;
12507
+ }
12508
+ const v = liveDefs.get(def);
12509
+ if (!v || !v.has(node)) {
12510
+ throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
12511
+ }
12512
+ v.delete(node);
12513
+ if (!v.size) {
12514
+ liveDefs.delete(def);
12515
+ }
12516
+ });
12517
+ }
12518
+ switch (node.type) {
12519
+ case "BinaryExpression":
12520
+ case "UnaryExpression":
12521
+ case "SizedArrayExpression":
12522
+ case "ArrayExpression":
12523
+ case "ObjectExpression":
12524
+ case "ThisExpression":
12525
+ case "LogicalExpression":
12526
+ case "ConditionalExpression":
12527
+ case "SequenceExpression":
12528
+ case "ParenthesizedExpression":
12529
+ break;
12530
+ case "Literal":
12531
+ if (!node.value && refCost(node) > LocalRefCost) {
12532
+ let decl = literals.get(node.value);
12533
+ if (!decl) {
12534
+ decl = node;
12535
+ literals.set(node.value, decl);
12536
+ }
12537
+ return {
12538
+ type: "ref",
12539
+ node,
12540
+ decl: decl,
12541
+ mayThrow,
12542
+ };
12543
+ }
12544
+ break;
12545
+ case "Identifier":
12546
+ identifiers.add(node.name);
12547
+ // fall through
12548
+ case "MemberExpression":
12549
+ {
12550
+ const decl = findDecl(node);
12551
+ if (decl && decl.type === "VariableDeclarator") {
12552
+ const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
12553
+ liveDefs.get(decl);
12554
+ if (defStmts) {
12555
+ break;
12556
+ /*
12557
+ // hold off on this for now. we need to communicate
12558
+ // which defs need to be fixed, which involves yet-another
12559
+ // table.
12560
+
12561
+ if (defStmts.size !== 1) break;
12562
+ const fixable = isFixableStmt([...defStmts][0]);
12563
+ if (fixable === false) break;
12564
+ cost += fixable;
12565
+ */
12566
+ }
12567
+ return {
12568
+ type: "ref",
12569
+ node,
12570
+ decl,
12571
+ mayThrow,
12572
+ };
12573
+ }
12574
+ }
12575
+ break;
12576
+ case "VariableDeclarator": {
12577
+ const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
12578
+ if (decl) {
12579
+ liveDef(decl, stmt);
12580
+ return {
12581
+ type: "def",
12582
+ node,
12583
+ decl,
12584
+ mayThrow,
12585
+ };
12586
+ }
12587
+ break;
12588
+ }
12589
+ case "AssignmentExpression": {
12590
+ const decl = findDecl(node.left);
12591
+ if (decl) {
12592
+ liveDef(decl, stmt);
12593
+ return {
12594
+ type: "def",
12595
+ node,
12596
+ decl,
12597
+ mayThrow,
12598
+ };
12599
+ }
12600
+ break;
12601
+ }
12602
+ case "UpdateExpression": {
12603
+ const decl = findDecl(node.argument);
12604
+ if (decl) {
12605
+ liveDef(decl, stmt);
12606
+ return {
12607
+ type: "def",
12608
+ node,
12609
+ decl,
12610
+ mayThrow,
12611
+ };
12612
+ }
12613
+ break;
12614
+ }
12615
+ case "NewExpression":
12616
+ case "CallExpression":
12617
+ liveDef(null, stmt);
12618
+ return { type: "mod", node, mayThrow };
12619
+ default:
12620
+ if (!isExpression(node))
12621
+ break;
12622
+ unhandledExpression(node);
12623
+ }
12624
+ if (mayThrow) {
12625
+ return { type: "exn", node, mayThrow };
12626
+ }
12627
+ return null;
12628
+ }),
12629
+ };
12630
+ }
12631
+ function anticipatedDecls() {
12632
+ return new Map();
12633
+ }
12634
+ function cloneSet(ae) {
12635
+ return new Set(ae);
12636
+ }
12637
+ function mergeSet(a, b) {
12638
+ b.forEach((event) => a.add(event));
12639
+ }
12640
+ function equalSet(a, b) {
12641
+ if (a.size != b.size)
12642
+ return false;
12643
+ for (const item of a) {
12644
+ if (!b.has(item))
12645
+ return false;
12646
+ }
12647
+ return true;
12648
+ }
12649
+ function equalMap(a, b) {
12650
+ if (a.size != b.size)
12651
+ return false;
12652
+ for (const [item, value] of a) {
12653
+ if (b.get(item) !== value)
12654
+ return false;
12655
+ }
12656
+ return true;
12657
+ }
12658
+ function anticipatedState(node, events) {
12659
+ return { ant: events || new Set(), live: true, node, members: new Map() };
12660
+ }
12661
+ function cloneAnticipatedState(as) {
12662
+ return {
12663
+ ant: cloneSet(as.ant),
12664
+ live: as.live,
12665
+ node: as.node,
12666
+ members: new Map(as.members),
12667
+ };
12668
+ }
12669
+ function mergeAnticipatedState(ae, be) {
12670
+ mergeSet(ae.ant, be.ant);
12671
+ be.members.forEach((live, block) => ae.members.set(block, live));
12672
+ if (be.live)
12673
+ ae.live = true;
12674
+ }
12675
+ function cloneAnticipatedDecls(ad) {
12676
+ const copy = anticipatedDecls();
12677
+ for (const [k, v] of ad) {
12678
+ if (!v.isIsolated) {
12679
+ copy.set(k, cloneAnticipatedState(v));
12680
+ }
12681
+ }
12682
+ return copy;
12683
+ }
12684
+ function mergeAnticipatedDecls(a, b) {
12685
+ for (const [k, v] of b) {
12686
+ if (v.isIsolated)
12687
+ continue;
12688
+ const ae = a.get(k);
12689
+ if (ae) {
12690
+ mergeAnticipatedState(ae, v);
12691
+ }
12692
+ else {
12693
+ a.set(k, cloneAnticipatedState(v));
12694
+ }
12695
+ }
12696
+ }
12697
+ function equalStates(a, b) {
12698
+ if (a.size !== b.size)
12699
+ return false;
12700
+ for (const [k, ae] of a) {
12701
+ const be = b.get(k);
12702
+ if (!be ||
12703
+ be.live != ae.live ||
12704
+ be.isIsolated != ae.isIsolated ||
12705
+ !equalSet(ae.ant, be.ant) ||
12706
+ !equalMap(ae.members, be.members)) {
12707
+ return false;
12708
+ }
12709
+ }
12710
+ return true;
12711
+ }
12712
+ const LocalRefCost = 2;
12713
+ function refCost(node) {
12714
+ if (node.type === "Literal") {
12715
+ switch (typeof node.value) {
12716
+ case "string":
12717
+ return 5;
12718
+ case "number":
12719
+ return 5;
12720
+ case "boolean":
12721
+ return 2;
12722
+ default:
12723
+ if (node.value === null) {
12724
+ return 2;
12725
+ }
12726
+ return 0;
12727
+ }
12728
+ }
12729
+ // A read from a non-local identifier takes 8 bytes
12730
+ let cost = 8;
12731
+ if (node.type === "Identifier")
12732
+ return cost;
12733
+ while (true) {
12734
+ const next = node.object;
12735
+ if (next.type != "MemberExpression") {
12736
+ if (next.type != "ThisExpression") {
12737
+ cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
12738
+ }
12739
+ return cost;
12740
+ }
12741
+ node = next;
12742
+ cost += 6;
12743
+ }
12744
+ }
12745
+ function defCost(node) {
12746
+ return refCost(node) + 2;
12747
+ }
12748
+ function candidateBoundary(candState) {
12749
+ const boundary = new Set();
12750
+ candState.members.forEach((live, block) => {
12751
+ if (live && block !== candState.head) {
12752
+ if (block.preds) {
12753
+ block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
12754
+ }
12755
+ }
12756
+ });
12757
+ if (candState.live) {
12758
+ if (!candState.head) {
12759
+ throw new Error(`Missing head`);
12760
+ }
12761
+ boundary.add(candState.head);
12762
+ }
12763
+ return boundary;
12764
+ }
12765
+ function candidateCost(candState) {
12766
+ let cost = 0;
12767
+ candState.ant.forEach((event) => {
12768
+ if (event.type === "ref") {
12769
+ cost -= refCost(candState.node) - LocalRefCost;
12770
+ }
12771
+ else {
12772
+ cost += defCost(candState.node);
12773
+ }
12774
+ });
12775
+ const boundarySize = candidateBoundary(candState).size;
12776
+ cost += defCost(candState.node) * boundarySize;
12777
+ return cost;
12778
+ }
12779
+ function computeAttributes(head) {
12780
+ const order = getPostOrder(head);
12781
+ order.forEach((block, i) => {
12782
+ block.order = i;
12783
+ });
12784
+ if (logging) {
12785
+ order.forEach((block) => {
12786
+ console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
12787
+ .map((block) => block.order)
12788
+ .join(", ")}`);
12789
+ if (block.events) {
12790
+ block.events.forEach((event) => event.type !== "exn" &&
12791
+ console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
12792
+ }
12793
+ console.log(`Succs: ${(block.succs || [])
12794
+ .map((block) => block.order)
12795
+ .join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
12796
+ });
12797
+ }
12798
+ const enqueued = new Set();
12799
+ const queue = new priorityqueuejs((b, a) => (a.order || 0) - (b.order || 0));
12800
+ const enqueue = (block) => {
12801
+ if (!enqueued.has(block)) {
12802
+ enqueued.add(block);
12803
+ queue.enq(block);
12804
+ }
12805
+ };
12806
+ const dequeue = () => {
12807
+ const block = queue.deq();
12808
+ enqueued.delete(block);
12809
+ return block;
12810
+ };
12811
+ const blockStates = [];
12812
+ /*
12813
+ Algorithm
12814
+ =========
12815
+
12816
+ Process blocks in post-order, and the events in reverse
12817
+ order to collect the AnticipatedState at the start of each
12818
+ Block.
12819
+
12820
+ Then for each EventDecl find the best starting block.
12821
+ */
12822
+ const modMap = new Map();
12823
+ const getMod = (event, decl, id) => {
12824
+ if (id.type !== "Identifier" && id.type !== "MemberExpression") {
12825
+ throw new Error("Trying to modify a non-variable");
12826
+ }
12827
+ let eventMap = modMap.get(event);
12828
+ if (!eventMap) {
12829
+ modMap.set(event, (eventMap = new Map()));
12830
+ }
12831
+ let result = eventMap.get(decl);
12832
+ if (!result) {
12833
+ result = {
12834
+ type: "mod",
12835
+ node: event.node,
12836
+ decl,
12837
+ id,
12838
+ mayThrow: event.mayThrow,
12839
+ };
12840
+ eventMap.set(decl, result);
12841
+ }
12842
+ return result;
12843
+ };
12844
+ order.forEach((block) => enqueue(block));
12845
+ while (queue.size()) {
12846
+ const top = dequeue();
12847
+ if (top.order === undefined) {
12848
+ throw new Error(`Unreachable block was visited!`);
12849
+ }
12850
+ const curState = (top.succs &&
12851
+ top.succs.reduce((blockState, succ) => {
12852
+ const succState = blockStates[succ.order];
12853
+ if (succState) {
12854
+ if (!blockState) {
12855
+ blockState = cloneAnticipatedDecls(succState);
12856
+ }
12857
+ else {
12858
+ mergeAnticipatedDecls(blockState, succState);
12859
+ }
12860
+ }
12861
+ return blockState;
12862
+ }, null)) ||
12863
+ anticipatedDecls();
12864
+ if (top.events) {
12865
+ for (let i = top.events.length; i--;) {
12866
+ const event = top.events[i];
12867
+ if (event.mayThrow && top.exsucc) {
12868
+ const succState = blockStates[top.exsucc.order];
12869
+ if (succState) {
12870
+ mergeAnticipatedDecls(curState, succState);
12871
+ }
12872
+ }
12873
+ switch (event.type) {
12874
+ case "ref": {
12875
+ let candidates = curState.get(event.decl);
12876
+ if (!candidates) {
12877
+ candidates = anticipatedState(event.node);
12878
+ curState.set(event.decl, candidates);
12879
+ }
12880
+ candidates.ant.add(event);
12881
+ candidates.live = true;
12882
+ break;
12883
+ }
12884
+ case "mod": {
12885
+ curState.forEach((candidates, decl) => {
12886
+ if (decl.type === "VariableDeclarator" &&
12887
+ decl.node.kind === "var" &&
12888
+ candidates.live) {
12889
+ candidates.ant.add(getMod(event, decl, candidates.node));
12890
+ candidates.live = false;
12891
+ }
12892
+ });
12893
+ break;
12894
+ }
12895
+ case "def": {
12896
+ let candidates = curState.get(event.decl);
12897
+ const isUpdate = event.node.type === "UpdateExpression" ||
12898
+ (event.node.type === "AssignmentExpression" &&
12899
+ event.node.operator !== "=");
12900
+ if (!candidates) {
12901
+ const target = event.node.type === "AssignmentExpression"
12902
+ ? event.node.left
12903
+ : event.node.type === "UpdateExpression"
12904
+ ? event.node.argument
12905
+ : event.node.id.type === "BinaryExpression"
12906
+ ? event.node.id.left
12907
+ : event.node.id;
12908
+ candidates = anticipatedState(target);
12909
+ curState.set(event.decl, candidates);
12910
+ }
12911
+ if (isUpdate || candidates.live) {
12912
+ candidates.ant.add(event);
12913
+ }
12914
+ candidates.live = isUpdate;
12915
+ break;
12916
+ }
12917
+ }
12918
+ }
12919
+ }
12920
+ curState.forEach((antState) => {
12921
+ antState.head = top;
12922
+ antState.members.set(top, antState.live);
12923
+ if (!antState.live && candidateBoundary(antState).size === 0) {
12924
+ // we found a group that's isolated from the rest
12925
+ // of the function. Don't merge it with earlier
12926
+ // refs and defs, because we can take it or leave
12927
+ // it based on its own cost.
12928
+ antState.isIsolated = true;
12929
+ }
12930
+ });
12931
+ const oldState = blockStates[top.order];
12932
+ if (oldState && equalStates(oldState, curState)) {
12933
+ continue;
12934
+ }
12935
+ blockStates[top.order] = curState;
12936
+ if (logging) {
12937
+ console.log(`Updated block ${top.order}`);
12938
+ logAntDecls(curState);
12939
+ }
12940
+ if (top.preds) {
12941
+ top.preds.forEach((pred) => enqueue(pred));
12942
+ }
12943
+ }
12944
+ const candidateDecls = anticipatedDecls();
12945
+ blockStates.forEach((blockState, i) => {
12946
+ blockState &&
12947
+ blockState.forEach((events, decl) => {
12948
+ const cost = candidateCost(events);
12949
+ if (cost >= 0)
12950
+ return;
12951
+ const existing = candidateDecls.get(decl);
12952
+ if (!existing ||
12953
+ existing.isIsolated ||
12954
+ candidateCost(existing) > cost) {
12955
+ const boundary = candidateBoundary(events);
12956
+ if (!Array.from(boundary).every((block) => {
12957
+ if (block !== events.head && block.events) {
12958
+ if (events.node.type === "Literal") {
12959
+ return false;
12960
+ }
12961
+ let i = block.events.length;
12962
+ while (i--) {
12963
+ const event = block.events[i];
12964
+ if (event.type === "def" || event.type === "mod") {
12965
+ events.ant.add({
12966
+ type: "mod",
12967
+ node: event.node,
12968
+ decl,
12969
+ id: events.node,
12970
+ mayThrow: false,
12971
+ });
12972
+ events.members.set(block, false);
12973
+ return true;
12974
+ }
12975
+ }
12976
+ }
12977
+ const node = block.node;
12978
+ if (!node)
12979
+ return false;
12980
+ events.ant.add({
12981
+ type: "mod",
12982
+ node: node.type === "FunctionDeclaration" ? node.body : node,
12983
+ before: true,
12984
+ decl,
12985
+ id: events.node,
12986
+ mayThrow: false,
12987
+ });
12988
+ events.members.set(block, false);
12989
+ return true;
12990
+ })) {
12991
+ return;
12992
+ }
12993
+ events.live = false;
12994
+ if (existing && existing.isIsolated) {
12995
+ delete existing.isIsolated;
12996
+ mergeAnticipatedState(events, existing);
12997
+ }
12998
+ else if (candidateCost(events) != cost) {
12999
+ throw new Error(`cost of block ${i} changed`);
13000
+ }
13001
+ candidateDecls.set(decl, events);
13002
+ }
13003
+ });
13004
+ });
13005
+ if (candidateDecls.size) {
13006
+ return candidateDecls;
13007
+ }
13008
+ return null;
13009
+ }
13010
+ /*
13011
+ * Determine the cost of fixing a def under a statement.
13012
+ *
13013
+ * eg:
13014
+ *
13015
+ * if (foo()) {
13016
+ * bar(X.y);
13017
+ * } else {
13018
+ * baz(X.y);
13019
+ * }
13020
+ *
13021
+ * Here, we could pull out X.y as a local, but if foo might modify
13022
+ * X.y, we have nowhere to insert the temporary. But we can rewrite
13023
+ * it as:
13024
+ *
13025
+ * var tmp = foo();
13026
+ * if (tmp) {
13027
+ * bar(X.y);
13028
+ * } else {
13029
+ * baz(X.y);
13030
+ * }
13031
+ *
13032
+ * and now we can insert a temporary before the if, but it costs
13033
+ * 4 bytes to do so.
13034
+ *
13035
+ * We can do the same for switch statements unless (ugh!)
13036
+ * the cases might modify the decl too.
13037
+ *
13038
+ * eg
13039
+ *
13040
+ * switch (foo()) {
13041
+ * case bar(): ...
13042
+ * }
13043
+ *
13044
+ */
13045
+ function _isFixableStmt(node) {
13046
+ switch (node.type) {
13047
+ case "IfStatement":
13048
+ return 4;
13049
+ case "SwitchStatement":
13050
+ if (node.cases.every((c) => !c.test ||
13051
+ c.test.type === "Literal" ||
13052
+ c.test.type === "Identifier" ||
13053
+ c.test.type === "InstanceOfCase" ||
13054
+ (c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
13055
+ return 4;
13056
+ }
13057
+ break;
13058
+ }
13059
+ return false;
13060
+ }
13061
+ function applyReplacements(func, nodeMap, declMap) {
13062
+ const ident = (name, node) => {
13063
+ return withLoc({ type: "Identifier", name }, node);
13064
+ };
13065
+ const pendingMap = new Map();
13066
+ const stmtStack = [func];
13067
+ traverseAst(func, (node) => {
13068
+ if (isStatement(node)) {
13069
+ stmtStack.push(node);
13070
+ }
13071
+ }, (node) => {
13072
+ const stmt = stmtStack[stmtStack.length - 1];
13073
+ if (stmt === node)
13074
+ stmtStack.pop();
13075
+ const events = nodeMap.get(node);
13076
+ if (events) {
13077
+ if (events.length === 1) {
13078
+ if (events[0].type === "ref") {
13079
+ if (node.type !== "Identifier" &&
13080
+ node.type !== "MemberExpression" &&
13081
+ node.type !== "Literal") {
13082
+ throw new Error(`Ref found, but wrong type of node: ${node.type}`);
13083
+ }
13084
+ const name = declMap.get(events[0].decl);
13085
+ if (!name) {
13086
+ throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(node)}"`);
13087
+ }
13088
+ return ident(name, node);
13089
+ }
13090
+ else if (events[0].type === "def") {
13091
+ if (node.type !== "AssignmentExpression" &&
13092
+ node.type !== "UpdateExpression") {
13093
+ throw new Error(`Def found, but wrong type of node: ${node.type}`);
13094
+ }
13095
+ const target = node.type === "AssignmentExpression"
13096
+ ? node.left
13097
+ : node.argument;
13098
+ const name = declMap.get(events[0].decl);
13099
+ if (!name) {
13100
+ throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(target)}"`);
13101
+ }
13102
+ const id = ident(name, target);
13103
+ const assign = withLoc({
13104
+ type: "AssignmentExpression",
13105
+ left: target,
13106
+ right: { ...id },
13107
+ operator: "=",
13108
+ }, node);
13109
+ if (node.type === "AssignmentExpression") {
13110
+ node.left = id;
13111
+ }
13112
+ else {
13113
+ node.argument = id;
13114
+ }
13115
+ return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
13116
+ }
13117
+ }
13118
+ events.forEach((event) => {
13119
+ if (event.type !== "mod") {
13120
+ throw new Error(`Unexpected ${event.type} found amongst multiple events`);
13121
+ }
13122
+ if (!event.decl) {
13123
+ throw new Error(`Unexpected null decl on mod event`);
13124
+ }
13125
+ let pending = pendingMap.get(stmt);
13126
+ if (!pending) {
13127
+ pendingMap.set(stmt, (pending = new Set()));
13128
+ }
13129
+ pending.add(event);
13130
+ });
13131
+ }
13132
+ const pending = pendingMap.get(node);
13133
+ if (node.type === "SequenceExpression") {
13134
+ if (pending) {
13135
+ throw new Error(`Unexpected pending list at SequenceExpression`);
13136
+ }
13137
+ for (let i = node.expressions.length; i--;) {
13138
+ const ni = node.expressions[i];
13139
+ if (ni.type === "SequenceExpression") {
13140
+ node.expressions.splice(i, 1, ...ni.expressions);
13141
+ }
13142
+ }
13143
+ }
13144
+ const applyPending = (results, locNode) => {
13145
+ const target = results.length === 1 && results[0].type === "BlockStatement"
13146
+ ? results[0]
13147
+ : null;
13148
+ pendingMap.delete(node);
13149
+ pending.forEach((event) => {
13150
+ const decl = event.decl;
13151
+ const name = declMap.get(decl);
13152
+ if (!name) {
13153
+ throw new Error(`No replacement found for "${declFullName(decl)}"`);
13154
+ }
13155
+ if (!event.id) {
13156
+ throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
13157
+ }
13158
+ const rhs = withLocDeep(event.id, locNode, locNode);
13159
+ rhs.end = rhs.start;
13160
+ if (rhs.loc) {
13161
+ rhs.loc.end = rhs.loc.start;
13162
+ }
13163
+ const insertion = withLoc({
13164
+ type: "ExpressionStatement",
13165
+ expression: withLoc({
13166
+ type: "AssignmentExpression",
13167
+ left: ident(name, rhs),
13168
+ right: rhs,
13169
+ operator: "=",
13170
+ }, rhs),
13171
+ }, rhs);
13172
+ if (event.type === "mod" && event.before) {
13173
+ if (target) {
13174
+ target.body.unshift(insertion);
13175
+ }
13176
+ else {
13177
+ results.unshift(insertion);
13178
+ }
13179
+ }
13180
+ else {
13181
+ results.push(insertion);
13182
+ }
13183
+ });
13184
+ return results.length === 1 ? null : results;
13185
+ };
13186
+ if (node.type === "ExpressionStatement" &&
13187
+ node.expression.type === "SequenceExpression") {
13188
+ const results = [];
13189
+ node.expression.expressions.forEach((expression) => {
13190
+ results.push({ ...node, expression });
13191
+ });
13192
+ if (!pending) {
13193
+ return results;
13194
+ }
13195
+ return applyPending(results, node);
13196
+ }
13197
+ if (pending) {
13198
+ return applyPending([node], node);
13199
+ }
13200
+ return null;
13201
+ });
13202
+ }
13203
+
13204
+ ;// CONCATENATED MODULE: ./src/visitor.ts
13205
+
13206
+ function visitReferences(state, ast, name, defn, callback) {
13207
+ const checkResults = ([name, results], node) => {
13208
+ if (name && results) {
13209
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
13210
+ if (callback(node, results, false) === false) {
13211
+ return [];
13212
+ }
13213
+ }
13214
+ }
13215
+ else if (defn === false) {
13216
+ if (callback(node, [], results === null) === false) {
13217
+ return [];
13218
+ }
13219
+ }
13220
+ return null;
13221
+ };
13222
+ state.pre = (node) => {
13223
+ switch (node.type) {
13224
+ case "AttributeList":
13225
+ return [];
13226
+ case "UnaryExpression":
13227
+ // a bare symbol isn't a reference
13228
+ if (node.operator === ":")
13229
+ return [];
13230
+ break;
13231
+ case "BinaryExpression":
13232
+ /*
13233
+ * `expr has :symbol` can be treated as a reference
13234
+ * to expr.symbol.
13235
+ */
13236
+ if (node.operator === "has") {
13237
+ if (node.right.type === "UnaryExpression" &&
13238
+ node.right.operator === ":") {
13239
+ if (!name || node.right.argument.name === name) {
13240
+ return checkResults(state.lookup({
13241
+ type: "MemberExpression",
13242
+ object: node.left,
13243
+ property: node.right.argument,
13244
+ computed: false,
13245
+ }), node.right.argument);
13246
+ }
13247
+ }
13248
+ }
13249
+ break;
13250
+ case "CallExpression":
13251
+ // A call expression whose callee is an identifier is looked
13252
+ // up as a non-local. ie even if there's a same named local,
13253
+ // it will be ignored, and the lookup will start as if the
13254
+ // call had been written self.foo() rather than foo().
13255
+ if (node.callee.type === "Identifier") {
13256
+ if (!name || node.callee.name === name) {
13257
+ /* ignore return value */
13258
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
13259
+ }
13260
+ return ["arguments"];
13261
+ }
13262
+ break;
13263
+ case "Identifier":
13264
+ if (!name || node.name === name) {
13265
+ return checkResults(state.lookup(node), node);
13266
+ }
13267
+ break;
13268
+ case "MemberExpression":
13269
+ if (!node.computed && node.property.type === "Identifier") {
13270
+ if (!name || node.property.name === name) {
13271
+ return checkResults(state.lookup(node), node) || ["object"];
13272
+ }
13273
+ return ["object"];
13274
+ }
13275
+ break;
13276
+ case "MethodDefinition": {
13277
+ if (!state.inType) {
13278
+ throw new Error("Method definition outside of type!");
13279
+ }
13280
+ if (node.params) {
13281
+ node.params.forEach((param) => {
13282
+ if (param.type == "BinaryExpression") {
13283
+ state.traverse(param.right);
13284
+ state.inType = true;
11606
13285
  }
11607
13286
  });
11608
13287
  }
@@ -11624,6 +13303,7 @@ function visitReferences(state, ast, name, defn, callback) {
11624
13303
 
11625
13304
 
11626
13305
 
13306
+
11627
13307
  function collectClassInfo(state) {
11628
13308
  const toybox = state.stack[0].decls["Toybox"][0];
11629
13309
  const lang = toybox.decls["Lang"][0];
@@ -11924,7 +13604,50 @@ function getNodeValue(node) {
11924
13604
  }
11925
13605
  return [node, type];
11926
13606
  }
11927
- function optimizeNode(node) {
13607
+ function fullTypeName(state, tsp) {
13608
+ if (typeof tsp.name === "string") {
13609
+ return tsp.name;
13610
+ }
13611
+ const [, results] = state.lookupType(tsp.name);
13612
+ if (results && results.length === 1 && results[0].results.length === 1) {
13613
+ const result = results[0].results[0];
13614
+ if ((0,external_api_cjs_namespaceObject.isStateNode)(result)) {
13615
+ return result.fullName;
13616
+ }
13617
+ }
13618
+ return null;
13619
+ }
13620
+ function isBooleanExpression(state, node) {
13621
+ switch (node.type) {
13622
+ case "Literal":
13623
+ return typeof node.value === "boolean";
13624
+ case "BinaryExpression":
13625
+ switch (node.operator) {
13626
+ case "==":
13627
+ case "!=":
13628
+ case "<=":
13629
+ case ">=":
13630
+ case "<":
13631
+ case ">":
13632
+ return true;
13633
+ case "as":
13634
+ return node.right.ts.length === 1 &&
13635
+ node.right.ts[0].type === "TypeSpecPart" &&
13636
+ node.right.ts[0].name &&
13637
+ fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
13638
+ ? true
13639
+ : false;
13640
+ }
13641
+ return false;
13642
+ case "LogicalExpression":
13643
+ return (isBooleanExpression(state, node.left) &&
13644
+ isBooleanExpression(state, node.right));
13645
+ case "UnaryExpression":
13646
+ return node.operator === "!" && isBooleanExpression(state, node.argument);
13647
+ }
13648
+ return false;
13649
+ }
13650
+ function optimizeNode(state, node) {
11928
13651
  switch (node.type) {
11929
13652
  case "UnaryExpression": {
11930
13653
  const [arg, type] = getNodeValue(node.argument);
@@ -11976,8 +13699,18 @@ function optimizeNode(node) {
11976
13699
  "%": (left, right) => left % right,
11977
13700
  "&": (left, right, type) => type === "Number" ? left & right : null,
11978
13701
  "|": (left, right, type) => type === "Number" ? left | right : null,
13702
+ "^": (left, right, type) => type === "Number" ? left ^ right : null,
11979
13703
  "<<": (left, right, type) => type === "Number" ? left << right : null,
11980
13704
  ">>": (left, right, type) => type === "Number" ? left >> right : null,
13705
+ "==": (left, right) => left == right,
13706
+ "!=": (left, right) => left != right,
13707
+ "<=": (left, right) => left <= right,
13708
+ ">=": (left, right) => left >= right,
13709
+ "<": (left, right) => left < right,
13710
+ ">": (left, right) => left > right,
13711
+ as: null,
13712
+ instanceof: null,
13713
+ has: null,
11981
13714
  };
11982
13715
  const op = operators[node.operator];
11983
13716
  if (op) {
@@ -11985,11 +13718,17 @@ function optimizeNode(node) {
11985
13718
  const [right, right_type] = getNodeValue(node.right);
11986
13719
  if (!left || !right)
11987
13720
  break;
13721
+ let value = null;
11988
13722
  if (left_type != right_type ||
11989
13723
  (left_type != "Number" && left_type != "Long")) {
11990
- break;
13724
+ if (node.operator !== "==" && node.operator !== "!=") {
13725
+ break;
13726
+ }
13727
+ value = operators[node.operator](left.value, right.value);
13728
+ }
13729
+ else {
13730
+ value = op(left.value, right.value, left_type);
11991
13731
  }
11992
- const value = op(left.value, right.value, left_type);
11993
13732
  if (value === null)
11994
13733
  break;
11995
13734
  return {
@@ -12000,15 +13739,47 @@ function optimizeNode(node) {
12000
13739
  }
12001
13740
  break;
12002
13741
  }
13742
+ case "LogicalExpression": {
13743
+ const [left, left_type] = getNodeValue(node.left);
13744
+ if (!left)
13745
+ break;
13746
+ const falsy = left.value === false ||
13747
+ left.value === null ||
13748
+ (left.value === 0 && (left_type === "Number" || left_type === "Long"));
13749
+ if (falsy === (node.operator === "&&")) {
13750
+ return left;
13751
+ }
13752
+ if (left_type !== "Boolean" &&
13753
+ left_type !== "Number" &&
13754
+ left_type !== "Long") {
13755
+ break;
13756
+ }
13757
+ const [right, right_type] = getNodeValue(node.right);
13758
+ if (right && right_type === left_type) {
13759
+ if (left_type === "Boolean" || node.operator === "||") {
13760
+ return right;
13761
+ }
13762
+ if (node.operator !== "&&") {
13763
+ throw new Error(`Unexpected operator "${node.operator}"`);
13764
+ }
13765
+ return { ...node, type: "BinaryExpression", operator: "&" };
13766
+ }
13767
+ if (left_type === "Boolean") {
13768
+ if (isBooleanExpression(state, node.right)) {
13769
+ return node.right;
13770
+ }
13771
+ }
13772
+ break;
13773
+ }
12003
13774
  case "FunctionDeclaration":
12004
- if (node.body && evaluateFunction(node, null) !== false) {
13775
+ if (node.body && evaluateFunction(state, node, null) !== false) {
12005
13776
  node.optimizable = true;
12006
13777
  }
12007
13778
  break;
12008
13779
  }
12009
13780
  return null;
12010
13781
  }
12011
- function evaluateFunction(func, args) {
13782
+ function evaluateFunction(state, func, args) {
12012
13783
  if (!func.body || (args && args.length != func.params.length)) {
12013
13784
  return false;
12014
13785
  }
@@ -12047,7 +13818,7 @@ function evaluateFunction(func, args) {
12047
13818
  }
12048
13819
  // fall through;
12049
13820
  default: {
12050
- const repl = optimizeNode(node);
13821
+ const repl = optimizeNode(state, node);
12051
13822
  if (repl && repl.type === "Literal")
12052
13823
  return repl;
12053
13824
  throw new Error("Didn't optimize");
@@ -12107,10 +13878,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12107
13878
  if (!objects) {
12108
13879
  return false;
12109
13880
  }
12110
- const obj = getLiteralFromDecls(objects);
13881
+ let obj = getLiteralFromDecls(objects);
12111
13882
  if (!obj) {
12112
13883
  return false;
12113
13884
  }
13885
+ while (obj.type === "BinaryExpression") {
13886
+ if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
13887
+ obj = { ...obj, left: obj.left.left };
13888
+ }
13889
+ else {
13890
+ obj = { ...obj, left: { ...obj.left } };
13891
+ break;
13892
+ }
13893
+ }
12114
13894
  inPlaceReplacement(node, obj);
12115
13895
  return true;
12116
13896
  };
@@ -12328,14 +14108,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12328
14108
  return null;
12329
14109
  };
12330
14110
  state.post = (node) => {
12331
- if (topLocals().node === node) {
14111
+ const locals = topLocals();
14112
+ if (locals.node === node) {
12332
14113
  state.localsStack.pop();
12333
14114
  }
12334
- const opt = optimizeNode(node);
14115
+ const opt = optimizeNode(state, node);
12335
14116
  if (opt) {
12336
14117
  return replace(opt, node);
12337
14118
  }
12338
14119
  switch (node.type) {
14120
+ case "BlockStatement":
14121
+ if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
14122
+ node.body.splice(0, 1, ...node.body[0].body);
14123
+ }
14124
+ break;
12339
14125
  case "ConditionalExpression":
12340
14126
  case "IfStatement":
12341
14127
  if (node.test.type === "Literal" &&
@@ -12345,6 +14131,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12345
14131
  return false;
12346
14132
  return replace(rep, rep);
12347
14133
  }
14134
+ else if (node.type === "IfStatement" &&
14135
+ node.alternate &&
14136
+ node.alternate.type === "BlockStatement" &&
14137
+ !node.alternate.body.length) {
14138
+ delete node.alternate;
14139
+ }
12348
14140
  break;
12349
14141
  case "WhileStatement":
12350
14142
  if (node.test.type === "Literal" && node.test.value === false) {
@@ -12372,6 +14164,48 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12372
14164
  return { type: "Literal", value: null, raw: "null" };
12373
14165
  }
12374
14166
  break;
14167
+ case "VariableDeclaration": {
14168
+ const locals = topLocals();
14169
+ if (locals.map &&
14170
+ locals.node &&
14171
+ locals.node.type === "BlockStatement") {
14172
+ let results;
14173
+ const declarations = node.declarations;
14174
+ let i = 0;
14175
+ let j = 0;
14176
+ while (i < node.declarations.length) {
14177
+ const decl = declarations[i++];
14178
+ if (decl.init && decl.init.type === "CallExpression") {
14179
+ const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
14180
+ if (!inlined)
14181
+ continue;
14182
+ if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
14183
+ throw new Error("Unexpected inlined result");
14184
+ }
14185
+ if (!results) {
14186
+ results = [];
14187
+ }
14188
+ delete decl.init;
14189
+ results.push(withLoc({
14190
+ ...node,
14191
+ declarations: declarations.slice(j, i),
14192
+ }, j ? declarations[j] : null, decl.id));
14193
+ results.push(inlined);
14194
+ j = i;
14195
+ }
14196
+ }
14197
+ if (results) {
14198
+ if (j < i) {
14199
+ results.push({
14200
+ ...node,
14201
+ declarations: declarations.slice(j, i),
14202
+ });
14203
+ }
14204
+ return results;
14205
+ }
14206
+ }
14207
+ break;
14208
+ }
12375
14209
  case "ExpressionStatement":
12376
14210
  if (node.expression.type === "CallExpression") {
12377
14211
  return replace(optimizeCall(state, node.expression, node), node.expression);
@@ -12416,6 +14250,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12416
14250
  });
12417
14251
  delete state.pre;
12418
14252
  delete state.post;
14253
+ state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
12419
14254
  const cleanup = (node) => {
12420
14255
  switch (node.type) {
12421
14256
  case "ThisExpression":
@@ -12541,7 +14376,7 @@ function optimizeCall(state, node, context) {
12541
14376
  callee.optimizable &&
12542
14377
  !callee.hasOverride &&
12543
14378
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12544
- const ret = evaluateFunction(callee, node.arguments);
14379
+ const ret = evaluateFunction(state, callee, node.arguments);
12545
14380
  if (ret) {
12546
14381
  return ret;
12547
14382
  }
@@ -12602,7 +14437,7 @@ function pragmaChecker(ast, diagnostics) {
12602
14437
  if (quote == '"') {
12603
14438
  return haystack.includes(needle);
12604
14439
  }
12605
- const re = new RegExp(needle);
14440
+ const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
12606
14441
  return re.test(haystack);
12607
14442
  };
12608
14443
  next();
@@ -12612,8 +14447,10 @@ function pragmaChecker(ast, diagnostics) {
12612
14447
  if (node.start && node.start >= (comment.end || Infinity)) {
12613
14448
  const { kind, quote, needle } = matchers.shift();
12614
14449
  if (kind === "match") {
12615
- if (!matcher(quote, needle, (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " "))) {
12616
- throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
14450
+ const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
14451
+ if (!matcher(quote, needle, haystack)) {
14452
+ matcher(quote, needle, haystack);
14453
+ throw new Error(`Didn't find '${needle}' in '${haystack}' at ${comment.loc.source}:${comment.loc.start.line}`);
12617
14454
  }
12618
14455
  }
12619
14456
  else if (kind === "expect") {
@@ -13054,6 +14891,7 @@ const configOptionsToCheck = [
13054
14891
  "ignoredAnnotations",
13055
14892
  "ignoredSourcePaths",
13056
14893
  "checkInvalidSymbols",
14894
+ "sizeBasedPRE",
13057
14895
  ];
13058
14896
  /**
13059
14897
  * @param {BuildConfig} config
@@ -13105,7 +14943,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
13105
14943
  // the oldest optimized file, we don't need to regenerate
13106
14944
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
13107
14945
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
13108
- if (source_time < opt_time && 1655851904193 < opt_time) {
14946
+ if (source_time < opt_time && 1656860602542 < opt_time) {
13109
14947
  return { hasTests, diagnostics: prevDiagnostics };
13110
14948
  }
13111
14949
  }