@markw65/monkeyc-optimizer 1.0.28 → 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/api.cjs CHANGED
@@ -1,8 +1,211 @@
1
- 0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiMapping,hasProperty,isStateNode,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
1
+ 0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
2
2
  /******/ (() => { // webpackBootstrap
3
- /******/ "use strict";
4
- /******/ // The require scope
5
- /******/ var __webpack_require__ = {};
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ 2789:
6
+ /***/ ((module) => {
7
+
8
+ /**
9
+ * Expose `PriorityQueue`.
10
+ */
11
+ module.exports = PriorityQueue;
12
+
13
+ /**
14
+ * Initializes a new empty `PriorityQueue` with the given `comparator(a, b)`
15
+ * function, uses `.DEFAULT_COMPARATOR()` when no function is provided.
16
+ *
17
+ * The comparator function must return a positive number when `a > b`, 0 when
18
+ * `a == b` and a negative number when `a < b`.
19
+ *
20
+ * @param {Function}
21
+ * @return {PriorityQueue}
22
+ * @api public
23
+ */
24
+ function PriorityQueue(comparator) {
25
+ this._comparator = comparator || PriorityQueue.DEFAULT_COMPARATOR;
26
+ this._elements = [];
27
+ }
28
+
29
+ /**
30
+ * Compares `a` and `b`, when `a > b` it returns a positive number, when
31
+ * it returns 0 and when `a < b` it returns a negative number.
32
+ *
33
+ * @param {String|Number} a
34
+ * @param {String|Number} b
35
+ * @return {Number}
36
+ * @api public
37
+ */
38
+ PriorityQueue.DEFAULT_COMPARATOR = function(a, b) {
39
+ if (typeof a === 'number' && typeof b === 'number') {
40
+ return a - b;
41
+ } else {
42
+ a = a.toString();
43
+ b = b.toString();
44
+
45
+ if (a == b) return 0;
46
+
47
+ return (a > b) ? 1 : -1;
48
+ }
49
+ };
50
+
51
+ /**
52
+ * Returns whether the priority queue is empty or not.
53
+ *
54
+ * @return {Boolean}
55
+ * @api public
56
+ */
57
+ PriorityQueue.prototype.isEmpty = function() {
58
+ return this.size() === 0;
59
+ };
60
+
61
+ /**
62
+ * Peeks at the top element of the priority queue.
63
+ *
64
+ * @return {Object}
65
+ * @throws {Error} when the queue is empty.
66
+ * @api public
67
+ */
68
+ PriorityQueue.prototype.peek = function() {
69
+ if (this.isEmpty()) throw new Error('PriorityQueue is empty');
70
+
71
+ return this._elements[0];
72
+ };
73
+
74
+ /**
75
+ * Dequeues the top element of the priority queue.
76
+ *
77
+ * @return {Object}
78
+ * @throws {Error} when the queue is empty.
79
+ * @api public
80
+ */
81
+ PriorityQueue.prototype.deq = function() {
82
+ var first = this.peek();
83
+ var last = this._elements.pop();
84
+ var size = this.size();
85
+
86
+ if (size === 0) return first;
87
+
88
+ this._elements[0] = last;
89
+ var current = 0;
90
+
91
+ while (current < size) {
92
+ var largest = current;
93
+ var left = (2 * current) + 1;
94
+ var right = (2 * current) + 2;
95
+
96
+ if (left < size && this._compare(left, largest) >= 0) {
97
+ largest = left;
98
+ }
99
+
100
+ if (right < size && this._compare(right, largest) >= 0) {
101
+ largest = right;
102
+ }
103
+
104
+ if (largest === current) break;
105
+
106
+ this._swap(largest, current);
107
+ current = largest;
108
+ }
109
+
110
+ return first;
111
+ };
112
+
113
+ /**
114
+ * Enqueues the `element` at the priority queue and returns its new size.
115
+ *
116
+ * @param {Object} element
117
+ * @return {Number}
118
+ * @api public
119
+ */
120
+ PriorityQueue.prototype.enq = function(element) {
121
+ var size = this._elements.push(element);
122
+ var current = size - 1;
123
+
124
+ while (current > 0) {
125
+ var parent = Math.floor((current - 1) / 2);
126
+
127
+ if (this._compare(current, parent) <= 0) break;
128
+
129
+ this._swap(parent, current);
130
+ current = parent;
131
+ }
132
+
133
+ return size;
134
+ };
135
+
136
+ /**
137
+ * Returns the size of the priority queue.
138
+ *
139
+ * @return {Number}
140
+ * @api public
141
+ */
142
+ PriorityQueue.prototype.size = function() {
143
+ return this._elements.length;
144
+ };
145
+
146
+ /**
147
+ * Iterates over queue elements
148
+ *
149
+ * @param {Function} fn
150
+ */
151
+ PriorityQueue.prototype.forEach = function(fn) {
152
+ return this._elements.forEach(fn);
153
+ };
154
+
155
+ /**
156
+ * Compares the values at position `a` and `b` in the priority queue using its
157
+ * comparator function.
158
+ *
159
+ * @param {Number} a
160
+ * @param {Number} b
161
+ * @return {Number}
162
+ * @api private
163
+ */
164
+ PriorityQueue.prototype._compare = function(a, b) {
165
+ return this._comparator(this._elements[a], this._elements[b]);
166
+ };
167
+
168
+ /**
169
+ * Swaps the values at position `a` and `b` in the priority queue.
170
+ *
171
+ * @param {Number} a
172
+ * @param {Number} b
173
+ * @api private
174
+ */
175
+ PriorityQueue.prototype._swap = function(a, b) {
176
+ var aux = this._elements[a];
177
+ this._elements[a] = this._elements[b];
178
+ this._elements[b] = aux;
179
+ };
180
+
181
+
182
+ /***/ })
183
+
184
+ /******/ });
185
+ /************************************************************************/
186
+ /******/ // The module cache
187
+ /******/ var __webpack_module_cache__ = {};
188
+ /******/
189
+ /******/ // The require function
190
+ /******/ function __webpack_require__(moduleId) {
191
+ /******/ // Check if module is in cache
192
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
193
+ /******/ if (cachedModule !== undefined) {
194
+ /******/ return cachedModule.exports;
195
+ /******/ }
196
+ /******/ // Create a new module (and put it into the cache)
197
+ /******/ var module = __webpack_module_cache__[moduleId] = {
198
+ /******/ // no module.id needed
199
+ /******/ // no module.loaded needed
200
+ /******/ exports: {}
201
+ /******/ };
202
+ /******/
203
+ /******/ // Execute the module function
204
+ /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
205
+ /******/
206
+ /******/ // Return the exports of the module
207
+ /******/ return module.exports;
208
+ /******/ }
6
209
  /******/
7
210
  /************************************************************************/
8
211
  /******/ /* webpack/runtime/compat get default export */
@@ -47,6 +250,9 @@
47
250
  /******/
48
251
  /************************************************************************/
49
252
  var __webpack_exports__ = {};
253
+ // This entry need to be wrapped in an IIFE because it need to be in strict mode.
254
+ (() => {
255
+ "use strict";
50
256
  // ESM COMPAT FLAG
51
257
  __webpack_require__.r(__webpack_exports__);
52
258
 
@@ -55,9 +261,12 @@ __webpack_require__.d(__webpack_exports__, {
55
261
  "collectNamespaces": () => (/* binding */ api_collectNamespaces),
56
262
  "findUsingForNode": () => (/* binding */ findUsingForNode),
57
263
  "formatAst": () => (/* binding */ api_formatAst),
264
+ "getApiFunctionInfo": () => (/* binding */ api_getApiFunctionInfo),
58
265
  "getApiMapping": () => (/* binding */ api_getApiMapping),
59
- "hasProperty": () => (/* binding */ api_hasProperty),
266
+ "hasProperty": () => (/* reexport */ ast_hasProperty),
267
+ "isLookupCandidate": () => (/* binding */ api_isLookupCandidate),
60
268
  "isStateNode": () => (/* binding */ api_isStateNode),
269
+ "markInvokeClassMethod": () => (/* binding */ api_markInvokeClassMethod),
61
270
  "sameLookupResult": () => (/* binding */ api_sameLookupResult),
62
271
  "traverseAst": () => (/* reexport */ ast_traverseAst),
63
272
  "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
@@ -86,62 +295,68 @@ function _check(x) {
86
295
  x = y;
87
296
  }
88
297
  const mctreeTypeInfo = {
89
- ArrayExpression: ["elements"],
90
- AssignmentExpression: ["left", "right"],
91
- AttributeList: ["attributes"],
92
- Attributes: ["elements"],
93
- BinaryExpression: ["left", "right"],
94
- Block: [],
95
- BlockStatement: ["body", "innerComments"],
96
- BreakStatement: [],
97
- CallExpression: ["callee", "arguments"],
98
- CatchClause: ["param", "body"],
99
- CatchClauses: ["catches"],
100
- ClassBody: ["body"],
101
- ClassDeclaration: ["attrs", "id", "superClass", "body"],
102
- ClassElement: ["item"],
103
- ConditionalExpression: ["test", "consequent", "alternate"],
104
- ContinueStatement: [],
105
- DoWhileStatement: ["body", "test"],
106
- EnumDeclaration: ["attrs", "id", "body"],
107
- EnumStringBody: ["members"],
108
- EnumStringMember: ["id", "init"],
109
- ExpressionStatement: ["expression"],
110
- ForStatement: ["init", "test", "body", "update"],
111
- FunctionDeclaration: ["attrs", "id", "params", "returnType", "body"],
112
- Identifier: [],
113
- IfStatement: ["test", "consequent", "alternate"],
114
- ImportModule: ["id"],
115
- InstanceOfCase: ["id"],
116
- Line: [],
117
- Literal: [],
118
- LogicalExpression: ["left", "right"],
119
- MemberExpression: ["object", "property"],
120
- MethodDefinition: ["params", "returnType"],
121
- ModuleDeclaration: ["attrs", "id", "body"],
122
- MultiLine: [],
123
- NewExpression: ["callee", "arguments"],
124
- ObjectExpression: ["properties"],
125
- ParenthesizedExpression: ["expression"],
126
- Program: ["body", "comments"],
127
- Property: ["key", "value"],
128
- ReturnStatement: ["argument"],
129
- SequenceExpression: ["expressions"],
130
- SizedArrayExpression: ["size", "ts"],
131
- SwitchCase: ["test", "consequent"],
132
- SwitchStatement: ["discriminant", "cases"],
133
- ThisExpression: [],
134
- ThrowStatement: ["argument"],
135
- TryStatement: ["block", "handler", "finalizer"],
136
- TypedefDeclaration: ["attrs", "id", "ts"],
137
- TypeSpecList: ["ts"],
138
- TypeSpecPart: ["name", "body", "callspec", "generics"],
139
- UnaryExpression: ["argument"],
140
- UpdateExpression: ["argument"],
141
- Using: ["id", "as"],
142
- VariableDeclaration: ["attrs", "declarations"],
143
- VariableDeclarator: ["id", "init"],
144
- WhileStatement: ["test", "body"],
298
+ ArrayExpression: { keys: ["elements"], expr: true },
299
+ AssignmentExpression: { keys: ["left", "right"], expr: true },
300
+ AttributeList: { keys: ["attributes"] },
301
+ Attributes: { keys: ["elements"] },
302
+ BinaryExpression: { keys: ["left", "right"], expr: true },
303
+ Block: { keys: [] },
304
+ BlockStatement: { keys: ["body", "innerComments"], stmt: true },
305
+ BreakStatement: { keys: [], stmt: true },
306
+ CallExpression: { keys: ["callee", "arguments"], expr: true },
307
+ CatchClause: { keys: ["param", "body"] },
308
+ CatchClauses: { keys: ["catches"] },
309
+ ClassBody: { keys: ["body"] },
310
+ ClassDeclaration: { keys: ["attrs", "id", "superClass", "body"], stmt: true },
311
+ ClassElement: { keys: ["item"] },
312
+ ConditionalExpression: {
313
+ keys: ["test", "consequent", "alternate"],
314
+ expr: true,
315
+ },
316
+ ContinueStatement: { keys: [], stmt: true },
317
+ DoWhileStatement: { keys: ["body", "test"], stmt: true },
318
+ EnumDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
319
+ EnumStringBody: { keys: ["members"] },
320
+ EnumStringMember: { keys: ["id", "init"] },
321
+ ExpressionStatement: { keys: ["expression"], stmt: true },
322
+ ForStatement: { keys: ["init", "test", "body", "update"], stmt: true },
323
+ FunctionDeclaration: {
324
+ keys: ["attrs", "id", "params", "returnType", "body"],
325
+ stmt: true,
326
+ },
327
+ Identifier: { keys: [], expr: true },
328
+ IfStatement: { keys: ["test", "consequent", "alternate"], stmt: true },
329
+ ImportModule: { keys: ["id"] },
330
+ InstanceOfCase: { keys: ["id"] },
331
+ Line: { keys: [] },
332
+ Literal: { keys: [], expr: true },
333
+ LogicalExpression: { keys: ["left", "right"], expr: true },
334
+ MemberExpression: { keys: ["object", "property"], expr: true },
335
+ MethodDefinition: { keys: ["params", "returnType"] },
336
+ ModuleDeclaration: { keys: ["attrs", "id", "body"], stmt: true },
337
+ MultiLine: { keys: [] },
338
+ NewExpression: { keys: ["callee", "arguments"], expr: true },
339
+ ObjectExpression: { keys: ["properties"], expr: true },
340
+ ParenthesizedExpression: { keys: ["expression"], expr: true },
341
+ Program: { keys: ["body", "comments"] },
342
+ Property: { keys: ["key", "value"] },
343
+ ReturnStatement: { keys: ["argument"], stmt: true },
344
+ SequenceExpression: { keys: ["expressions"], expr: true },
345
+ SizedArrayExpression: { keys: ["size", "ts"], expr: true },
346
+ SwitchCase: { keys: ["test", "consequent"] },
347
+ SwitchStatement: { keys: ["discriminant", "cases"], stmt: true },
348
+ ThisExpression: { keys: [], expr: true },
349
+ ThrowStatement: { keys: ["argument"], stmt: true },
350
+ TryStatement: { keys: ["block", "handler", "finalizer"], stmt: true },
351
+ TypedefDeclaration: { keys: ["attrs", "id", "ts"], stmt: true },
352
+ TypeSpecList: { keys: ["ts"] },
353
+ TypeSpecPart: { keys: ["name", "body", "callspec", "generics"] },
354
+ UnaryExpression: { keys: ["argument"], expr: true },
355
+ UpdateExpression: { keys: ["argument"], expr: true },
356
+ Using: { keys: ["id", "as"] },
357
+ VariableDeclaration: { keys: ["attrs", "declarations"], stmt: true },
358
+ VariableDeclarator: { keys: ["id", "init"] },
359
+ WhileStatement: { keys: ["test", "body"], stmt: true },
145
360
  };
146
361
  function isMCTreeNode(node) {
147
362
  return node ? typeof node === "object" && "type" in node : false;
@@ -166,7 +381,7 @@ function ast_traverseAst(node, pre, post) {
166
381
  if (!mctreeTypeInfo[node.type]) {
167
382
  throw new Error("what?");
168
383
  }
169
- for (const key of nodes || mctreeTypeInfo[node.type]) {
384
+ for (const key of nodes || mctreeTypeInfo[node.type].keys) {
170
385
  const value = node[key];
171
386
  if (!value)
172
387
  continue;
@@ -193,13 +408,21 @@ function ast_traverseAst(node, pre, post) {
193
408
  }
194
409
  }
195
410
  else if (isMCTreeNode(value)) {
196
- const repl = ast_traverseAst(value, pre, post);
411
+ let repl = ast_traverseAst(value, pre, post);
197
412
  if (repl === false) {
198
413
  delete node[key];
199
414
  }
200
415
  else if (repl != null) {
201
416
  if (Array.isArray(repl)) {
202
- throw new Error("Array returned by traverseAst in Node context");
417
+ if (ast_isStatement(value) && repl.every((s) => ast_isStatement(s))) {
418
+ repl = ast_withLoc({
419
+ type: "BlockStatement",
420
+ body: repl,
421
+ }, repl[0], repl[repl.length - 1]);
422
+ }
423
+ else {
424
+ throw new Error("Array returned by traverseAst in Node context");
425
+ }
203
426
  }
204
427
  node[key] = repl;
205
428
  }
@@ -207,9 +430,187 @@ function ast_traverseAst(node, pre, post) {
207
430
  }
208
431
  return post && post(node);
209
432
  }
433
+ function ast_isStatement(node) {
434
+ return ast_hasProperty(mctreeTypeInfo[node.type], "stmt");
435
+ }
436
+ function ast_isExpression(node) {
437
+ return ast_hasProperty(mctreeTypeInfo[node.type], "expr");
438
+ }
439
+ function ast_mayThrow(node) {
440
+ switch (node.type) {
441
+ case "BinaryExpression":
442
+ case "CallExpression":
443
+ case "ConditionalExpression":
444
+ case "LogicalExpression":
445
+ case "NewExpression":
446
+ case "ThrowStatement":
447
+ case "UnaryExpression":
448
+ case "UpdateExpression":
449
+ return true;
450
+ default:
451
+ return false;
452
+ }
453
+ }
454
+ function ast_hasProperty(obj, prop) {
455
+ return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
456
+ }
457
+ function ast_withLoc(node, start, end) {
458
+ if (start && start.loc) {
459
+ node.start = start.start;
460
+ if (!node.end)
461
+ node.end = start.end;
462
+ node.loc = { ...(node.loc || start.loc), start: start.loc.start };
463
+ }
464
+ if (end && end.loc) {
465
+ node.end = end.end;
466
+ node.loc = { ...(node.loc || end.loc), end: end.loc.end };
467
+ }
468
+ return node;
469
+ }
470
+ function ast_withLocDeep(node, start, end, inplace) {
471
+ node = ast_withLoc(inplace ? node : { ...node }, start, end);
472
+ for (const key of mctreeTypeInfo[node.type].keys) {
473
+ const value = node[key];
474
+ if (!value)
475
+ continue;
476
+ const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end, inplace) : v;
477
+ const repl = Array.isArray(value) ? value.map(fix) : fix(value);
478
+ inplace || (node[key] = repl);
479
+ }
480
+ return node;
481
+ }
482
+ function ast_cloneDeep(node) {
483
+ return ast_withLocDeep(node, null);
484
+ }
210
485
 
211
486
  ;// CONCATENATED MODULE: external "./api.cjs"
212
487
  const external_api_cjs_namespaceObject = require("./api.cjs");
488
+ ;// CONCATENATED MODULE: ./src/function-info.ts
489
+
490
+ function function_info_cloneSet(ae) {
491
+ return new Set(ae);
492
+ }
493
+ function function_info_mergeSet(a, b) {
494
+ b.forEach((event) => a.add(event));
495
+ }
496
+ function recordModifiedDecl(func, decl) {
497
+ if (!func.next_info) {
498
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
499
+ }
500
+ func.next_info.modifiedDecls.add(decl);
501
+ return null;
502
+ }
503
+ function function_info_recordModifiedDecls(func, lookupDefs) {
504
+ lookupDefs.forEach((lookupDef) => lookupDef.results.forEach((result) => {
505
+ if (result.type == "VariableDeclarator" && result.node.kind === "var") {
506
+ recordModifiedDecl(func, result);
507
+ }
508
+ }));
509
+ }
510
+ function function_info_recordModifiedName(func, name) {
511
+ if (!func.next_info) {
512
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
513
+ }
514
+ if (!func.next_info.modifiedNames) {
515
+ func.next_info.modifiedNames = new Set();
516
+ }
517
+ func.next_info.modifiedNames.add(name);
518
+ }
519
+ function function_info_recordModifiedUnknown(func) {
520
+ if (!func.next_info) {
521
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
522
+ }
523
+ func.next_info.modifiedUnknown = true;
524
+ }
525
+ function recordCalledFunc(func, callee) {
526
+ if (!func.next_info) {
527
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
528
+ }
529
+ func.next_info.calledFuncs.add(callee);
530
+ return null;
531
+ }
532
+ function function_info_recordCalledFuncs(func, callees) {
533
+ callees.forEach((callee) => {
534
+ recordCalledFunc(func, callee);
535
+ });
536
+ }
537
+ function function_info_functionMayModify(state, func, decl) {
538
+ const info = func.info;
539
+ if (!info || info.modifiedUnknown)
540
+ return true;
541
+ if (info.resolvedDecls) {
542
+ return info.resolvedDecls.has(decl);
543
+ }
544
+ if (info.modifiedNames?.has(decl.name))
545
+ return true;
546
+ if (info.modifiedDecls.has(decl))
547
+ return true;
548
+ const visited = new Set();
549
+ const resolved = new Set();
550
+ const resolveDecls = (f) => {
551
+ if (visited.has(f))
552
+ return true;
553
+ if (!f.info)
554
+ return false;
555
+ if (f.info.modifiedUnknown) {
556
+ info.modifiedUnknown = true;
557
+ return false;
558
+ }
559
+ if (f.info.modifiedNames) {
560
+ if (info.modifiedNames) {
561
+ function_info_mergeSet(info.modifiedNames, f.info.modifiedNames);
562
+ }
563
+ else {
564
+ info.modifiedNames = function_info_cloneSet(f.info.modifiedNames);
565
+ }
566
+ }
567
+ function_info_mergeSet(resolved, f.info.modifiedDecls);
568
+ visited.add(f);
569
+ const q = true;
570
+ if (q &&
571
+ f.info.callsExposed &&
572
+ state.exposed &&
573
+ !Object.keys(state.exposed).every((key) => !state.allFunctions[key] ||
574
+ state.allFunctions[key].every(resolveDecls))) {
575
+ return false;
576
+ }
577
+ return Array.from(f.info.calledFuncs).every(resolveDecls);
578
+ };
579
+ if (resolveDecls(func)) {
580
+ info.resolvedDecls = resolved;
581
+ return resolved.has(decl);
582
+ }
583
+ return true;
584
+ }
585
+ function function_info_findCallees(lookupDefs) {
586
+ const decls = lookupDefs.reduce((decls, r) => (decls ? decls.concat(r.results) : r.results), null);
587
+ return (decls &&
588
+ decls.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
589
+ }
590
+ function function_info_findCalleesForNew(lookupDefs) {
591
+ const initializer = (decl) => {
592
+ if (hasProperty(decl.decls, "initialize")) {
593
+ return decl.decls["initialize"];
594
+ }
595
+ if (decl.superClass && decl.superClass !== true) {
596
+ return decl.superClass.reduce((cur, cls) => {
597
+ const init = initializer(cls);
598
+ if (init) {
599
+ if (!cur)
600
+ return init;
601
+ return cur.concat(init);
602
+ }
603
+ return cur;
604
+ }, null);
605
+ }
606
+ return null;
607
+ };
608
+ return lookupDefs.flatMap((r) => r.results
609
+ .filter((decl) => decl.type === "ClassDeclaration")
610
+ .flatMap(initializer)
611
+ .filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
612
+ }
613
+
213
614
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
214
615
 
215
616
 
@@ -270,6 +671,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
270
671
 
271
672
 
272
673
 
674
+
273
675
  function getArgSafety(state, func, args, requireAll) {
274
676
  // determine whether decl might be changed by a function call
275
677
  // or assignment during the evaluation of FunctionStateNode.
@@ -302,8 +704,9 @@ function getArgSafety(state, func, args, requireAll) {
302
704
  }
303
705
  };
304
706
  const safeArgs = [];
707
+ const argDecls = [];
305
708
  let allSafe = true;
306
- if (!args.every((arg) => {
709
+ if (!args.every((arg, i) => {
307
710
  switch (arg.type) {
308
711
  case "Literal":
309
712
  safeArgs.push(true);
@@ -317,13 +720,17 @@ function getArgSafety(state, func, args, requireAll) {
317
720
  safeArgs.push(null);
318
721
  return !requireAll;
319
722
  }
320
- const safety = getSafety(results[0].results[0]);
723
+ const decl = results[0].results[0];
724
+ const safety = getSafety(decl);
321
725
  safeArgs.push(safety);
322
726
  if (!safety) {
323
727
  allSafe = false;
324
728
  if (safety === null) {
325
729
  return !requireAll;
326
730
  }
731
+ else if (decl.type === "VariableDeclarator") {
732
+ argDecls[i] = decl;
733
+ }
327
734
  }
328
735
  return true;
329
736
  }
@@ -336,34 +743,91 @@ function getArgSafety(state, func, args, requireAll) {
336
743
  }
337
744
  if (allSafe && requireAll)
338
745
  return true;
339
- let callSeen = false;
746
+ const callsSeen = new Set();
747
+ const modifiedDecls = new Set();
748
+ let modifiedUnknown = false;
340
749
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
341
750
  // look for uses of "unsafe" args that occur after a call.
342
751
  // use post to do the checking, because arguments are evaluated
343
752
  // prior to the call, so eg "return f(x.y);" is fine, but
344
753
  // "return f()+x.y" is not.
345
- traverseAst(func.node.body, null, (node) => {
346
- switch (node.type) {
347
- case "AssignmentExpression":
348
- case "UpdateExpression": {
349
- const v = node.type == "UpdateExpression" ? node.argument : node.left;
350
- if (v.type === "Identifier" && hasProperty(params, v.name)) {
351
- safeArgs[params[v.name]] = null;
754
+ const { pre, post, stack } = state;
755
+ try {
756
+ delete state.pre;
757
+ state.post = (node) => {
758
+ switch (node.type) {
759
+ case "AssignmentExpression":
760
+ case "UpdateExpression": {
761
+ const v = node.type == "UpdateExpression" ? node.argument : node.left;
762
+ if (v.type === "Identifier" && hasProperty(params, v.name)) {
763
+ // If a parameter is modified, we can't just substitute the
764
+ // argument wherever the parameter is used.
765
+ safeArgs[params[v.name]] = null;
766
+ break;
767
+ }
768
+ if (modifiedUnknown)
769
+ break;
770
+ const [, results] = state.lookup(v);
771
+ if (results) {
772
+ results.forEach((r) => r.results.forEach((decl) => decl.type === "VariableDeclarator" && modifiedDecls.add(decl)));
773
+ }
774
+ else {
775
+ modifiedUnknown = true;
776
+ }
777
+ break;
352
778
  }
779
+ case "CallExpression":
780
+ case "NewExpression":
781
+ if (!modifiedUnknown) {
782
+ const [, results] = state.lookup(node.callee, null,
783
+ // calls are looked up as non-locals, but new is not
784
+ node.type === "CallExpression" ? func.stack : state.stack);
785
+ if (!results) {
786
+ const callee_name = node.callee.type === "Identifier"
787
+ ? node.callee
788
+ : node.callee.type === "MemberExpression"
789
+ ? isLookupCandidate(node.callee)
790
+ : null;
791
+ if (callee_name) {
792
+ const callees = state.allFunctions[callee_name.name];
793
+ if (callees) {
794
+ callees.forEach((callee) => callsSeen.add(callee));
795
+ }
796
+ }
797
+ else {
798
+ modifiedUnknown = true;
799
+ }
800
+ }
801
+ else {
802
+ const callees = node.type === "CallExpression"
803
+ ? findCallees(results)
804
+ : findCalleesForNew(results);
805
+ if (callees) {
806
+ callees.forEach((callee) => callsSeen.add(callee));
807
+ }
808
+ }
809
+ }
810
+ break;
811
+ case "Identifier":
812
+ if (hasProperty(params, node.name) &&
813
+ !safeArgs[params[node.name]] &&
814
+ (modifiedUnknown ||
815
+ !argDecls[params[node.name]] ||
816
+ modifiedDecls.has(argDecls[params[node.name]]) ||
817
+ Array.from(callsSeen).some((callee) => functionMayModify(state, callee, argDecls[params[node.name]])))) {
818
+ safeArgs[params[node.name]] = null;
819
+ }
353
820
  }
354
- // fall through
355
- case "CallExpression":
356
- case "NewExpression":
357
- callSeen = true;
358
- break;
359
- case "Identifier":
360
- if (callSeen &&
361
- hasProperty(params, node.name) &&
362
- !safeArgs[params[node.name]]) {
363
- safeArgs[params[node.name]] = null;
364
- }
365
- }
366
- });
821
+ return null;
822
+ };
823
+ state.stack = func.stack;
824
+ state.traverse(func.node.body);
825
+ }
826
+ finally {
827
+ state.pre = pre;
828
+ state.post = post;
829
+ state.stack = stack;
830
+ }
367
831
  return safeArgs;
368
832
  }
369
833
  function canInline(state, func, args) {
@@ -468,9 +932,6 @@ function processInlineBody(state, func, call, root, params) {
468
932
  state.pre = (node) => {
469
933
  if (failed)
470
934
  return [];
471
- node.start = call.start;
472
- node.end = call.end;
473
- node.loc = call.loc;
474
935
  if (replacements.has(node))
475
936
  return false;
476
937
  const result = pre(node, state);
@@ -485,6 +946,7 @@ function processInlineBody(state, func, call, root, params) {
485
946
  if (params[paramName] >= 0)
486
947
  return null;
487
948
  const name = renameVariable(state, locals, paramName) || paramName;
949
+ locals.map[name] = true;
488
950
  return {
489
951
  type: "VariableDeclarator",
490
952
  id: { type: "Identifier", name },
@@ -503,31 +965,49 @@ function processInlineBody(state, func, call, root, params) {
503
965
  }
504
966
  return result;
505
967
  };
968
+ const fixId = (node) => {
969
+ if (state.inType)
970
+ return null;
971
+ if (hasProperty(params, node.name)) {
972
+ const ix = params[node.name];
973
+ if (ix >= 0) {
974
+ const replacement = { ...call.arguments[ix] };
975
+ replacements.add(replacement);
976
+ return replacement;
977
+ }
978
+ return null;
979
+ }
980
+ const replacement = fixNodeScope(state, node, func.stack);
981
+ if (!replacement) {
982
+ failed = true;
983
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
984
+ }
985
+ return replacement;
986
+ };
506
987
  state.post = (node) => {
507
988
  if (failed)
508
989
  return post(node, state);
509
990
  let replacement = null;
510
991
  switch (node.type) {
511
- case "Identifier": {
512
- if (state.inType)
513
- break;
514
- if (hasProperty(params, node.name)) {
515
- const ix = params[node.name];
516
- if (ix >= 0) {
517
- replacement = call.arguments[ix];
518
- replacements.add(replacement);
519
- return replacement;
992
+ case "AssignmentExpression":
993
+ if (node.left.type === "Identifier") {
994
+ const rep = fixId(node.left);
995
+ if (rep) {
996
+ node.left = rep;
520
997
  }
521
- break;
522
998
  }
523
- replacement = fixNodeScope(state, node, func.stack);
524
- if (!replacement) {
525
- failed = true;
526
- inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
527
- return post(node, state);
999
+ break;
1000
+ case "UpdateExpression":
1001
+ if (node.argument.type === "Identifier") {
1002
+ const rep = fixId(node.argument);
1003
+ if (rep) {
1004
+ node.argument = rep;
1005
+ }
528
1006
  }
529
1007
  break;
530
- }
1008
+ case "Identifier":
1009
+ replacement = fixId(node);
1010
+ break;
531
1011
  }
532
1012
  const ret = post(replacement || node, state);
533
1013
  return ret === false || ret ? ret : replacement;
@@ -553,6 +1033,10 @@ function processInlineBody(state, func, call, root, params) {
553
1033
  }
554
1034
  }
555
1035
  function inliner_unused(expression, top) {
1036
+ const estmt = (expression) => withLoc({
1037
+ type: "ExpressionStatement",
1038
+ expression,
1039
+ }, expression);
556
1040
  switch (expression.type) {
557
1041
  case "Literal":
558
1042
  return [];
@@ -564,9 +1048,50 @@ function inliner_unused(expression, top) {
564
1048
  if (expression.operator === "as") {
565
1049
  return inliner_unused(expression.left);
566
1050
  }
567
- // fall through
568
- case "LogicalExpression":
569
1051
  return inliner_unused(expression.left).concat(inliner_unused(expression.right));
1052
+ case "LogicalExpression": {
1053
+ const right = inliner_unused(expression.right);
1054
+ if (!right.length)
1055
+ return inliner_unused(expression.left);
1056
+ const consequent = withLoc({
1057
+ type: "BlockStatement",
1058
+ body: [estmt(expression.right)],
1059
+ }, expression.right);
1060
+ let alternate;
1061
+ if (expression.operator == "||") {
1062
+ alternate = { ...consequent };
1063
+ consequent.body = [];
1064
+ }
1065
+ return [
1066
+ withLoc({
1067
+ type: "IfStatement",
1068
+ test: expression.left,
1069
+ consequent,
1070
+ alternate,
1071
+ }, expression),
1072
+ ];
1073
+ }
1074
+ case "ConditionalExpression": {
1075
+ const consequentExprs = inliner_unused(expression.consequent);
1076
+ const alternateExprs = inliner_unused(expression.alternate);
1077
+ if (!consequentExprs.length && !alternateExprs.length) {
1078
+ return inliner_unused(expression.test);
1079
+ }
1080
+ return [
1081
+ withLoc({
1082
+ type: "IfStatement",
1083
+ test: expression.test,
1084
+ consequent: withLoc({
1085
+ type: "BlockStatement",
1086
+ body: consequentExprs,
1087
+ }, expression.consequent),
1088
+ alternate: withLoc({
1089
+ type: "BlockStatement",
1090
+ body: alternateExprs,
1091
+ }, expression.alternate),
1092
+ }, expression),
1093
+ ];
1094
+ }
570
1095
  case "UnaryExpression":
571
1096
  return inliner_unused(expression.argument);
572
1097
  case "MemberExpression":
@@ -581,17 +1106,7 @@ function inliner_unused(expression, top) {
581
1106
  .map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
582
1107
  .flat(1);
583
1108
  }
584
- return top
585
- ? null
586
- : [
587
- {
588
- type: "ExpressionStatement",
589
- expression,
590
- start: expression.start,
591
- end: expression.end,
592
- loc: expression.loc,
593
- },
594
- ];
1109
+ return top ? null : [estmt(expression)];
595
1110
  }
596
1111
  function inliner_diagnostic(state, loc, message, type = "INFO") {
597
1112
  if (!loc || !loc.source)
@@ -624,6 +1139,10 @@ function inlineWithArgs(state, func, call, context) {
624
1139
  if (!func.node || !func.node.body) {
625
1140
  return null;
626
1141
  }
1142
+ const lastStmt = (block) => {
1143
+ const last = block.body.slice(-1)[0];
1144
+ return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
1145
+ };
627
1146
  let retStmtCount = 0;
628
1147
  if (context.type === "ReturnStatement") {
629
1148
  const last = func.node.body.body.slice(-1)[0];
@@ -639,21 +1158,25 @@ function inlineWithArgs(state, func, call, context) {
639
1158
  if (retStmtCount > 1) {
640
1159
  inlineDiagnostic(state, func, call, "Function had more than one return statement");
641
1160
  }
642
- else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
1161
+ else if ((context.type === "AssignmentExpression" ||
1162
+ context.type === "VariableDeclarator") &&
1163
+ retStmtCount !== 1) {
643
1164
  inlineDiagnostic(state, func, call, "Function did not have a return statement");
644
1165
  return null;
645
1166
  }
646
1167
  if (retStmtCount === 1) {
647
- const last = func.node.body.body.slice(-1)[0];
1168
+ const [last] = lastStmt(func.node.body);
648
1169
  if (!last ||
649
1170
  last.type !== "ReturnStatement" ||
650
- (context.type === "AssignmentExpression" && !last.argument)) {
1171
+ ((context.type === "AssignmentExpression" ||
1172
+ context.type === "VariableDeclarator") &&
1173
+ !last.argument)) {
651
1174
  inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
652
1175
  return null;
653
1176
  }
654
1177
  }
655
1178
  }
656
- const body = JSON.parse(JSON.stringify(func.node.body));
1179
+ const body = cloneDeep(func.node.body);
657
1180
  const safeArgs = getArgSafety(state, func, call.arguments, false);
658
1181
  const params = Object.fromEntries(func.node.params.map((param, i) => {
659
1182
  const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
@@ -667,37 +1190,54 @@ function inlineWithArgs(state, func, call, context) {
667
1190
  }
668
1191
  inliner_diagnostic(state, call.loc, null);
669
1192
  if (context.type !== "ReturnStatement" && retStmtCount) {
670
- const last = body.body[body.body.length - 1];
1193
+ const [last, block] = lastStmt(body);
671
1194
  if (last.type != "ReturnStatement") {
672
1195
  throw new Error("ReturnStatement got lost!");
673
1196
  }
674
- if (context.type === "AssignmentExpression") {
675
- context.right = last.argument;
676
- body.body[body.body.length - 1] = {
677
- type: "ExpressionStatement",
678
- expression: context,
679
- };
680
- }
681
- else if (last.argument) {
682
- const side_exprs = inliner_unused(last.argument);
683
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
1197
+ if (last.argument) {
1198
+ if (context.type === "AssignmentExpression") {
1199
+ context.right = last.argument;
1200
+ block.body[block.body.length - 1] = {
1201
+ type: "ExpressionStatement",
1202
+ expression: context,
1203
+ };
1204
+ }
1205
+ else if (context.type === "VariableDeclarator") {
1206
+ const { id, init: _init, kind: _kind, ...rest } = context;
1207
+ block.body[block.body.length - 1] = {
1208
+ ...rest,
1209
+ type: "ExpressionStatement",
1210
+ expression: {
1211
+ ...rest,
1212
+ type: "AssignmentExpression",
1213
+ operator: "=",
1214
+ left: id.type === "Identifier" ? id : id.left,
1215
+ right: last.argument,
1216
+ },
1217
+ };
1218
+ }
1219
+ else {
1220
+ const side_exprs = inliner_unused(last.argument);
1221
+ block.body.splice(block.body.length - 1, 1, ...side_exprs);
1222
+ }
684
1223
  }
685
1224
  else {
686
- --body.body.length;
1225
+ --block.body.length;
687
1226
  }
688
1227
  }
1228
+ withLocDeep(body, context, context, true);
689
1229
  return body;
690
1230
  }
691
1231
  function inliner_inlineFunction(state, func, call, context) {
692
1232
  if (context) {
693
1233
  return inlineWithArgs(state, func, call, context);
694
1234
  }
695
- const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
1235
+ const retArg = cloneDeep(func.node.body.body[0].argument);
696
1236
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
697
1237
  const map = fixupLocalsMap(state);
698
1238
  const ret = processInlineBody(state, func, call, retArg, params);
699
1239
  state.localsStack[state.localsStack.length - 1].map = map;
700
- return ret;
1240
+ return ret && withLocDeep(ret, call, call, true);
701
1241
  }
702
1242
  function applyTypeIfNeeded(node) {
703
1243
  if ("enumType" in node && node.enumType) {
@@ -799,11 +1339,1660 @@ function fixNodeScope(state, lookupNode, nodeStack) {
799
1339
  loc: node.loc,
800
1340
  }, node));
801
1341
  }
802
- return null;
1342
+ return null;
1343
+ }
1344
+
1345
+ ;// CONCATENATED MODULE: ./src/pragma-checker.ts
1346
+
1347
+
1348
+
1349
+ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1350
+ const comments = ast.comments;
1351
+ if (!comments)
1352
+ return;
1353
+ diagnostics = diagnostics
1354
+ ?.slice()
1355
+ .sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
1356
+ let diagIndex = 0;
1357
+ let index = -1;
1358
+ let comment;
1359
+ let matchers;
1360
+ const next = () => {
1361
+ while (++index < comments.length) {
1362
+ comment = comments[index];
1363
+ let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
1364
+ if (!match)
1365
+ continue;
1366
+ const kind = match[1];
1367
+ let str = match[2];
1368
+ matchers = [];
1369
+ while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
1370
+ matchers.push({ kind, quote: match[1], needle: match[2] });
1371
+ str = str.substring(match[0].length);
1372
+ if (!str.length)
1373
+ break;
1374
+ }
1375
+ if (!str.length)
1376
+ break;
1377
+ if (!matchers.length) {
1378
+ match = str.match(/^(\S+)\s+$/);
1379
+ if (match) {
1380
+ matchers.push({ kind, quote: '"', needle: match[1] });
1381
+ break;
1382
+ }
1383
+ }
1384
+ diagnostic(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
1385
+ }
1386
+ };
1387
+ const matcher = (quote, needle, haystack) => {
1388
+ if (quote == '"') {
1389
+ return haystack.includes(needle);
1390
+ }
1391
+ const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
1392
+ return re.test(haystack);
1393
+ };
1394
+ next();
1395
+ traverseAst(ast, (node) => {
1396
+ if (index >= comments.length)
1397
+ return false;
1398
+ if (node.start && node.start >= (comment.end || Infinity)) {
1399
+ const { kind, quote, needle } = matchers.shift();
1400
+ if (kind === "match") {
1401
+ const haystack = formatAst(node).replace(/([\r\n]|\s)+/g, " ");
1402
+ if (!matcher(quote, needle, haystack)) {
1403
+ matcher(quote, needle, haystack);
1404
+ diagnostic(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
1405
+ }
1406
+ }
1407
+ else if (kind === "expect") {
1408
+ const locCmp = (a, b) => {
1409
+ if (!b)
1410
+ return -1;
1411
+ if (a.start.line < b.start.line)
1412
+ return -1;
1413
+ if (a.start.line === b.start.line &&
1414
+ a.start.column < b.start.column) {
1415
+ return -1;
1416
+ }
1417
+ if (a.end.line > b.end.line)
1418
+ return 1;
1419
+ if (a.end.line === b.end.line && a.end.column >= b.end.column) {
1420
+ return 1;
1421
+ }
1422
+ return 0;
1423
+ };
1424
+ let found = false;
1425
+ if (diagnostics) {
1426
+ while (true) {
1427
+ if (diagIndex >= diagnostics.length) {
1428
+ diagnostics = null;
1429
+ break;
1430
+ }
1431
+ const diag = diagnostics[diagIndex];
1432
+ const cmp = locCmp(diag.loc, node.loc);
1433
+ if (cmp > 0) {
1434
+ break;
1435
+ }
1436
+ diagIndex++;
1437
+ if (cmp < 0)
1438
+ continue;
1439
+ if (matcher(quote, needle, diag.message)) {
1440
+ found = true;
1441
+ diag.type = "INFO";
1442
+ }
1443
+ }
1444
+ }
1445
+ if (!found) {
1446
+ diagnostic(state, comment.loc, `Missing error message '${needle}`, "ERROR");
1447
+ }
1448
+ }
1449
+ if (matchers.length) {
1450
+ // if we're checking a series of nodes, we need
1451
+ // to skip over this one.
1452
+ return false;
1453
+ }
1454
+ next();
1455
+ }
1456
+ return null;
1457
+ });
1458
+ }
1459
+
1460
+ ;// CONCATENATED MODULE: external "./util.cjs"
1461
+ const external_util_cjs_namespaceObject = require("./util.cjs");
1462
+ ;// CONCATENATED MODULE: ./src/control-flow.ts
1463
+
1464
+
1465
+ const Terminals = {
1466
+ BreakStatement: "break",
1467
+ ContinueStatement: "continue",
1468
+ ReturnStatement: null,
1469
+ ThrowStatement: "throw",
1470
+ };
1471
+ class LocalState {
1472
+ constructor(func) {
1473
+ this.stack = [];
1474
+ this.info = new Map();
1475
+ this.curBlock = {};
1476
+ this.unreachable = false;
1477
+ this.push(func);
1478
+ }
1479
+ push(node) {
1480
+ const top = { node };
1481
+ this.stack.push(top);
1482
+ return top;
1483
+ }
1484
+ pop() {
1485
+ return this.stack.pop();
1486
+ }
1487
+ top(depth) {
1488
+ return this.stack[this.stack.length - (depth || 1)];
1489
+ }
1490
+ addEdge(from, to) {
1491
+ if (!from.succs) {
1492
+ from.succs = [to];
1493
+ }
1494
+ else {
1495
+ pushUnique(from.succs, to);
1496
+ }
1497
+ if (!to.preds) {
1498
+ to.preds = [from];
1499
+ }
1500
+ else {
1501
+ pushUnique(to.preds, from);
1502
+ }
1503
+ }
1504
+ newBlock(block) {
1505
+ if (!block)
1506
+ block = {};
1507
+ if (!this.unreachable) {
1508
+ this.addEdge(this.curBlock, block);
1509
+ }
1510
+ this.unreachable = false;
1511
+ for (let i = this.stack.length; i--;) {
1512
+ const si = this.stack[i];
1513
+ if (si.throw) {
1514
+ block.exsucc = si.throw;
1515
+ if (!si.throw.expreds) {
1516
+ si.throw.expreds = [block];
1517
+ }
1518
+ else {
1519
+ si.throw.expreds.push(block);
1520
+ }
1521
+ break;
1522
+ }
1523
+ }
1524
+ return (this.curBlock = block);
1525
+ }
1526
+ terminal(type) {
1527
+ const re = Terminals[type];
1528
+ if (re) {
1529
+ for (let i = this.stack.length; i--;) {
1530
+ const target = this.stack[i][re];
1531
+ if (target) {
1532
+ this.addEdge(this.curBlock, target);
1533
+ break;
1534
+ }
1535
+ }
1536
+ }
1537
+ this.unreachable = true;
1538
+ }
1539
+ }
1540
+ function control_flow_buildReducedGraph(state, func, notice) {
1541
+ const { stack, pre, post } = state;
1542
+ try {
1543
+ const localState = new LocalState(func.node);
1544
+ const ret = localState.curBlock;
1545
+ state.stack = func.stack;
1546
+ const stmtStack = [func.node];
1547
+ let tryActive = 0;
1548
+ state.pre = (node) => {
1549
+ if (state.inType || localState.unreachable) {
1550
+ return [];
1551
+ }
1552
+ if (!localState.curBlock.node &&
1553
+ (isStatement(node) || isExpression(node))) {
1554
+ localState.curBlock.node = node;
1555
+ }
1556
+ if (isStatement(node)) {
1557
+ stmtStack.push(node);
1558
+ }
1559
+ switch (node.type) {
1560
+ case "AttributeList":
1561
+ return [];
1562
+ case "SwitchStatement": {
1563
+ const top = localState.push(node);
1564
+ top.break = {};
1565
+ state.traverse(node.discriminant);
1566
+ const testBlocks = [];
1567
+ let defaultSeen = false;
1568
+ node.cases.forEach((sc, i) => {
1569
+ if (sc.test) {
1570
+ state.traverse(sc.test);
1571
+ testBlocks[i] = localState.curBlock;
1572
+ localState.newBlock();
1573
+ }
1574
+ else {
1575
+ defaultSeen = true;
1576
+ }
1577
+ });
1578
+ const endOfTests = localState.curBlock;
1579
+ if (!defaultSeen) {
1580
+ localState.addEdge(endOfTests, top.break);
1581
+ }
1582
+ localState.unreachable = true;
1583
+ node.cases.forEach((sc, i) => {
1584
+ localState.newBlock();
1585
+ localState.addEdge(testBlocks[i] || endOfTests, localState.curBlock);
1586
+ sc.consequent.every((s) => {
1587
+ state.traverse(s);
1588
+ return !localState.unreachable;
1589
+ });
1590
+ });
1591
+ localState.newBlock(top.break);
1592
+ localState.unreachable = !top.break.preds;
1593
+ return [];
1594
+ }
1595
+ case "DoWhileStatement":
1596
+ case "WhileStatement": {
1597
+ localState.push(node);
1598
+ const top = localState.top();
1599
+ top.break = {};
1600
+ top.continue = {};
1601
+ let head;
1602
+ if (node.type === "WhileStatement") {
1603
+ head = localState.newBlock(top.continue);
1604
+ state.traverse(node.test);
1605
+ localState.addEdge(localState.curBlock, top.break);
1606
+ localState.newBlock();
1607
+ }
1608
+ else {
1609
+ head = localState.newBlock();
1610
+ }
1611
+ state.traverse(node.body);
1612
+ if (node.type === "DoWhileStatement") {
1613
+ localState.newBlock(top.continue);
1614
+ state.traverse(node.test);
1615
+ localState.addEdge(localState.curBlock, top.break);
1616
+ }
1617
+ localState.addEdge(localState.curBlock, head);
1618
+ localState.curBlock = top.break;
1619
+ return [];
1620
+ }
1621
+ case "TryStatement": {
1622
+ const top = localState.push(node);
1623
+ const catches = (top.throw = {});
1624
+ // This edge shouldn't exist, but we can trigger
1625
+ // (incorrect) "variable may not be initialized" errors
1626
+ // in the monkey c compiler without it.
1627
+ // https://forums.garmin.com/developer/connect-iq/i/bug-reports/incorrect-maybe-uninitialized-error
1628
+ localState.addEdge(localState.curBlock, top.throw);
1629
+ localState.newBlock();
1630
+ tryActive++;
1631
+ state.traverse(node.block);
1632
+ tryActive--;
1633
+ delete top.throw;
1634
+ top.posttry = {};
1635
+ const tryFallsThrough = !localState.unreachable;
1636
+ if (node.finalizer) {
1637
+ tryActive++;
1638
+ top.throw = top.finally = {};
1639
+ // curBlock branches to finally, no matter how it exits.
1640
+ localState.addEdge(localState.curBlock, top.finally);
1641
+ }
1642
+ else {
1643
+ if (!localState.unreachable) {
1644
+ localState.addEdge(localState.curBlock, top.posttry);
1645
+ }
1646
+ }
1647
+ localState.unreachable = true;
1648
+ localState.newBlock(catches);
1649
+ if (node.handler) {
1650
+ state.traverse(node.handler);
1651
+ if (top.throw) {
1652
+ tryActive--;
1653
+ delete top.throw;
1654
+ }
1655
+ // Each "catch (ex instanceof Foo)" chains to the next,
1656
+ // but "catch (ex)" terminates the list. If the end
1657
+ // of the chain has a predecessor, its possible that
1658
+ // none of the conditions matched, so the exception
1659
+ // will propagate from there.
1660
+ if (localState.curBlock.preds) {
1661
+ localState.terminal("ThrowStatement");
1662
+ }
1663
+ }
1664
+ if (top.throw) {
1665
+ tryActive--;
1666
+ delete top.throw;
1667
+ }
1668
+ if (node.finalizer) {
1669
+ localState.unreachable = true;
1670
+ localState.newBlock(top.finally);
1671
+ delete top.finally;
1672
+ state.traverse(node.finalizer);
1673
+ if (tryFallsThrough && !localState.unreachable) {
1674
+ localState.addEdge(localState.curBlock, top.posttry);
1675
+ }
1676
+ localState.terminal("ThrowStatement");
1677
+ }
1678
+ localState.unreachable = true;
1679
+ localState.newBlock(top.posttry);
1680
+ return [];
1681
+ }
1682
+ case "CatchClause": {
1683
+ const top = localState.top();
1684
+ if (!localState.curBlock.preds && !localState.curBlock.expreds) {
1685
+ return [];
1686
+ }
1687
+ const next = {};
1688
+ if (node.param && node.param.type === "BinaryExpression") {
1689
+ state.traverse(node.param);
1690
+ localState.addEdge(localState.curBlock, next);
1691
+ localState.newBlock();
1692
+ }
1693
+ state.traverse(node.body);
1694
+ if (top.finally) {
1695
+ // this edge exists even if this point is unreachable
1696
+ localState.addEdge(localState.curBlock, top.finally);
1697
+ }
1698
+ if (!localState.unreachable) {
1699
+ if (!top.posttry)
1700
+ top.posttry = {};
1701
+ localState.addEdge(localState.curBlock, top.posttry);
1702
+ }
1703
+ localState.unreachable = true;
1704
+ localState.newBlock(next);
1705
+ return [];
1706
+ }
1707
+ case "ForStatement": {
1708
+ const top = localState.push(node);
1709
+ if (node.init)
1710
+ state.traverse(node.init);
1711
+ const head = localState.newBlock();
1712
+ top.break = {};
1713
+ top.continue = {};
1714
+ if (node.test) {
1715
+ state.traverse(node.test);
1716
+ localState.addEdge(localState.curBlock, top.break);
1717
+ localState.newBlock();
1718
+ }
1719
+ state.traverse(node.body);
1720
+ localState.newBlock(top.continue);
1721
+ if (node.update) {
1722
+ state.traverse(node.update);
1723
+ }
1724
+ if (!localState.unreachable) {
1725
+ localState.addEdge(localState.curBlock, head);
1726
+ }
1727
+ // there is no fall through from the end of the loop
1728
+ // to the next block. The only way there is via break
1729
+ // or the test failing.
1730
+ localState.unreachable = true;
1731
+ localState.newBlock(top.break);
1732
+ if (!top.break.preds) {
1733
+ localState.unreachable = true;
1734
+ }
1735
+ return [];
1736
+ }
1737
+ case "IfStatement":
1738
+ case "ConditionalExpression": {
1739
+ state.traverse(node.test);
1740
+ const alternate = {};
1741
+ localState.addEdge(localState.curBlock, alternate);
1742
+ localState.newBlock();
1743
+ state.traverse(node.consequent);
1744
+ const consequent = localState.unreachable
1745
+ ? null
1746
+ : localState.curBlock;
1747
+ localState.unreachable = true;
1748
+ localState.newBlock(alternate);
1749
+ if (node.alternate) {
1750
+ state.traverse(node.alternate);
1751
+ if (!localState.unreachable) {
1752
+ localState.newBlock();
1753
+ }
1754
+ }
1755
+ if (consequent) {
1756
+ if (localState.unreachable) {
1757
+ localState.newBlock();
1758
+ }
1759
+ localState.addEdge(consequent, localState.curBlock);
1760
+ }
1761
+ return [];
1762
+ }
1763
+ case "LogicalExpression": {
1764
+ state.traverse(node.left);
1765
+ if (localState.unreachable)
1766
+ break;
1767
+ const mid = localState.curBlock;
1768
+ localState.newBlock();
1769
+ state.traverse(node.right);
1770
+ localState.newBlock();
1771
+ localState.addEdge(mid, localState.curBlock);
1772
+ return [];
1773
+ }
1774
+ case "VariableDeclarator":
1775
+ return ["init"];
1776
+ case "MemberExpression":
1777
+ if (!node.computed) {
1778
+ return ["object"];
1779
+ }
1780
+ break;
1781
+ case "UnaryExpression":
1782
+ if (node.operator === ":") {
1783
+ return [];
1784
+ }
1785
+ break;
1786
+ case "UpdateExpression":
1787
+ // We don't want to traverse the argument, since then it would
1788
+ // look like a ref, rather than a def. But if its a
1789
+ // MemberExpression, we *do* want to traverse the subexpressions
1790
+ // as potential refs.
1791
+ if (node.argument.type === "MemberExpression") {
1792
+ state.traverse(node.argument.object);
1793
+ if (node.argument.computed) {
1794
+ state.traverse(node.argument.property);
1795
+ }
1796
+ }
1797
+ return [];
1798
+ case "AssignmentExpression":
1799
+ if (node.left.type === "MemberExpression") {
1800
+ state.traverse(node.left.object);
1801
+ if (node.left.computed) {
1802
+ state.traverse(node.left.property);
1803
+ }
1804
+ }
1805
+ return ["right"];
1806
+ case "ThrowStatement":
1807
+ case "ReturnStatement":
1808
+ if (node.argument) {
1809
+ state.traverse(node.argument);
1810
+ }
1811
+ // fall through
1812
+ case "BreakStatement":
1813
+ case "ContinueStatement":
1814
+ localState.terminal(node.type);
1815
+ return [];
1816
+ }
1817
+ return null;
1818
+ };
1819
+ const addEvent = (block, event) => {
1820
+ if (!block.events) {
1821
+ block.events = [event];
1822
+ }
1823
+ else {
1824
+ block.events.push(event);
1825
+ }
1826
+ };
1827
+ state.post = (node) => {
1828
+ const curStmt = stmtStack[stmtStack.length - 1];
1829
+ if (!state.inType) {
1830
+ const throws = tryActive > 0 && mayThrow(node);
1831
+ const event = notice(node, curStmt, throws);
1832
+ if (throws) {
1833
+ if (!event) {
1834
+ throw new Error("mayThrow expression in try/catch must generate an event");
1835
+ }
1836
+ }
1837
+ else if (event) {
1838
+ event.mayThrow = false;
1839
+ }
1840
+ if (event) {
1841
+ if (event.mayThrow) {
1842
+ for (let i = localState.stack.length; i--;) {
1843
+ const target = localState.stack[i].throw;
1844
+ if (target) {
1845
+ if (localState.curBlock.exsucc) {
1846
+ if (localState.curBlock.exsucc !== target) {
1847
+ throw new Error(`Block has multiple throw targets`);
1848
+ }
1849
+ }
1850
+ else {
1851
+ localState.curBlock.exsucc = target;
1852
+ if (!target.expreds) {
1853
+ target.expreds = [localState.curBlock];
1854
+ }
1855
+ else {
1856
+ target.expreds.push(localState.curBlock);
1857
+ }
1858
+ }
1859
+ break;
1860
+ }
1861
+ }
1862
+ }
1863
+ addEvent(localState.curBlock, event);
1864
+ }
1865
+ }
1866
+ if (curStmt === node) {
1867
+ stmtStack.pop();
1868
+ }
1869
+ if (localState.top().node === node) {
1870
+ localState.pop();
1871
+ }
1872
+ return null;
1873
+ };
1874
+ state.traverse(func.node);
1875
+ return cleanCfg(ret);
1876
+ }
1877
+ finally {
1878
+ state.pre = pre;
1879
+ state.post = post;
1880
+ state.stack = stack;
1881
+ }
1882
+ }
1883
+ function cleanCfg(head) {
1884
+ preOrderTraverse(head, (cur) => {
1885
+ if (cur.succs && cur.succs.length === 1) {
1886
+ const succ = cur.succs[0];
1887
+ if (succ !== head &&
1888
+ succ.preds.length === 1 &&
1889
+ (!cur.exsucc || cur.exsucc === succ.exsucc) &&
1890
+ (!succ.succs ||
1891
+ succ.succs.length === 1 ||
1892
+ (cur.preds && cur.preds.length === 1))) {
1893
+ if (cur.events) {
1894
+ if (succ.events) {
1895
+ cur.events.push(...succ.events);
1896
+ }
1897
+ }
1898
+ else if (succ.events) {
1899
+ cur.events = succ.events;
1900
+ }
1901
+ if (succ.exsucc) {
1902
+ const preds = succ.exsucc.expreds;
1903
+ for (let i = preds.length; i--;) {
1904
+ if (preds[i] === succ) {
1905
+ // If cur has an exsucc, we already
1906
+ // checked that its the same as succ's,
1907
+ // so we can just delete the edge.
1908
+ // Otherwise, we need to point it at cur.
1909
+ if (cur.exsucc) {
1910
+ preds.splice(i, 1);
1911
+ }
1912
+ else {
1913
+ preds[i] = cur;
1914
+ }
1915
+ }
1916
+ }
1917
+ }
1918
+ cur.exsucc = succ.exsucc;
1919
+ cur.succs = succ.succs;
1920
+ if (cur.succs) {
1921
+ cur.succs.forEach((s) => s.preds.forEach((p, i, arr) => {
1922
+ if (p === succ) {
1923
+ arr[i] = cur;
1924
+ }
1925
+ }));
1926
+ }
1927
+ if (!cur.node)
1928
+ cur.node = succ.node;
1929
+ }
1930
+ }
1931
+ });
1932
+ return head;
1933
+ }
1934
+ function postOrderTraverse(head, visitor) {
1935
+ const visited = new Set();
1936
+ const helper = (cur) => {
1937
+ if (visited.has(cur))
1938
+ return;
1939
+ visited.add(cur);
1940
+ if (cur.succs) {
1941
+ cur.succs.forEach((block) => helper(block));
1942
+ }
1943
+ if (cur.exsucc)
1944
+ helper(cur.exsucc);
1945
+ visitor(cur);
1946
+ };
1947
+ helper(head);
1948
+ }
1949
+ function preOrderTraverse(head, visitor) {
1950
+ const visited = new Set();
1951
+ const helper = (cur) => {
1952
+ if (visited.has(cur))
1953
+ return;
1954
+ visited.add(cur);
1955
+ visitor(cur);
1956
+ if (cur.succs) {
1957
+ cur.succs.forEach((block) => helper(block));
1958
+ }
1959
+ if (cur.exsucc)
1960
+ helper(cur.exsucc);
1961
+ };
1962
+ helper(head);
1963
+ }
1964
+ function control_flow_getPostOrder(head) {
1965
+ const blocks = [];
1966
+ postOrderTraverse(head, (block) => blocks.push(block));
1967
+ return blocks;
1968
+ }
1969
+ function getPreOrder(head) {
1970
+ const blocks = [];
1971
+ postOrderTraverse(head, (block) => blocks.push(block));
1972
+ return blocks;
1973
+ }
1974
+
1975
+ // EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
1976
+ var priorityqueuejs = __webpack_require__(2789);
1977
+ ;// CONCATENATED MODULE: ./src/pre.ts
1978
+
1979
+
1980
+
1981
+
1982
+
1983
+ /**
1984
+ * This implements a pseudo Partial Redundancy Elimination
1985
+ * pass. It isn't quite like traditional PRE because we're
1986
+ * aiming to minimize size, not dynamic instructions. So
1987
+ * for us, its worthwhile to take something like:
1988
+ *
1989
+ * switch (x) {
1990
+ * case 1: foo(A.B); break;
1991
+ * case 2: foo(C); break;
1992
+ * case 3: bar(A.B); break;
1993
+ * }
1994
+ *
1995
+ * and rewrite it as
1996
+ *
1997
+ * var tmp = A.B;
1998
+ * switch (x) {
1999
+ * case 1: foo(tmp); break;
2000
+ * case 2: foo(C); break;
2001
+ * case 3: bar(tmp); break;
2002
+ * }
2003
+ *
2004
+ * because even though A.B wasn't used on all paths where we
2005
+ * inserted the temporary, we still reduced the code size.
2006
+ */
2007
+ const logging = false;
2008
+ function declFullName(decl) {
2009
+ switch (decl.type) {
2010
+ case "Literal":
2011
+ return decl.raw || decl.value?.toString() || "null";
2012
+ case "VariableDeclarator":
2013
+ return decl.fullName;
2014
+ default:
2015
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
2016
+ }
2017
+ }
2018
+ function declName(decl) {
2019
+ switch (decl.type) {
2020
+ case "Literal":
2021
+ return (decl.raw || decl.value?.toString() || "null").replace(/[^\w]/g, "_");
2022
+ case "VariableDeclarator":
2023
+ return decl.name;
2024
+ default:
2025
+ throw new Error(`Unexpected EventDecl type: ${decl.type}`);
2026
+ }
2027
+ }
2028
+ function logAntState(s, decl) {
2029
+ const defs = Array.from(s.ant).reduce((defs, event) => {
2030
+ if (event.type === "def" || event.type === "mod")
2031
+ defs++;
2032
+ return defs;
2033
+ }, 0);
2034
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
2035
+ console.log(` - members: ${Array.from(s.members)
2036
+ .map(([block, live]) => block.order + (live ? "t" : "f"))
2037
+ .join(", ")}`);
2038
+ }
2039
+ function logAntDecls(antDecls) {
2040
+ antDecls.forEach(logAntState);
2041
+ }
2042
+ function pre_sizeBasedPRE(state, func) {
2043
+ if (!func.node.body)
2044
+ return;
2045
+ if (!state.config ||
2046
+ !state.config.sizeBasedPRE ||
2047
+ (typeof state.config.sizeBasedPRE === "string" &&
2048
+ state.config.sizeBasedPRE !== func.fullName)) {
2049
+ return;
2050
+ }
2051
+ const { graph: head, identifiers } = buildPREGraph(state, func);
2052
+ const candidates = computeAttributes(state, head);
2053
+ if (candidates) {
2054
+ if (logging) {
2055
+ console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
2056
+ logAntDecls(candidates);
2057
+ }
2058
+ const nodeMap = new Map();
2059
+ const declMap = new Map();
2060
+ const variableDecl = withLoc({
2061
+ type: "VariableDeclaration",
2062
+ declarations: [],
2063
+ kind: "var",
2064
+ }, func.node.body);
2065
+ variableDecl.end = variableDecl.start;
2066
+ variableDecl.loc.end = variableDecl.loc.start;
2067
+ candidates.forEach((s, decl) => {
2068
+ let name;
2069
+ let i = 0;
2070
+ do {
2071
+ name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
2072
+ if (!identifiers.has(name)) {
2073
+ identifiers.add(name);
2074
+ break;
2075
+ }
2076
+ i++;
2077
+ } while (true);
2078
+ declMap.set(decl, name);
2079
+ variableDecl.declarations.push(withLoc({
2080
+ type: "VariableDeclarator",
2081
+ id: withLoc({ type: "Identifier", name }, variableDecl),
2082
+ kind: "var",
2083
+ }, variableDecl));
2084
+ s.ant.forEach((event) => {
2085
+ const events = nodeMap.get(event.node);
2086
+ if (!events) {
2087
+ nodeMap.set(event.node, [event]);
2088
+ }
2089
+ else {
2090
+ events.push(event);
2091
+ }
2092
+ });
2093
+ });
2094
+ applyReplacements(func.node, nodeMap, declMap);
2095
+ func.node.body.body.unshift(variableDecl);
2096
+ }
2097
+ }
2098
+ function unhandledExpression(node) {
2099
+ throw new Error(`Unhandled expression type: ${node.type}`);
2100
+ }
2101
+ function buildPREGraph(state, func) {
2102
+ const findDecl = (node) => {
2103
+ if (node.type === "Identifier" ||
2104
+ (node.type === "MemberExpression" && !node.computed)) {
2105
+ const [, results] = state.lookup(node);
2106
+ if (results &&
2107
+ results.length === 1 &&
2108
+ results[0].parent?.type != "BlockStatement" &&
2109
+ results[0].results.length === 1 &&
2110
+ results[0].results[0].type === "VariableDeclarator") {
2111
+ return results[0].results[0];
2112
+ }
2113
+ }
2114
+ return null;
2115
+ };
2116
+ const literals = new Map();
2117
+ const identifiers = new Set();
2118
+ const liveDefs = new Map();
2119
+ const liveStmts = new Map();
2120
+ const liveDef = (def, stmt) => {
2121
+ let curNodes = liveDefs.get(def);
2122
+ if (!curNodes) {
2123
+ liveDefs.set(def, (curNodes = new Set()));
2124
+ }
2125
+ curNodes.add(stmt);
2126
+ let defs = liveStmts.get(stmt);
2127
+ if (!defs) {
2128
+ liveStmts.set(stmt, (defs = new Map()));
2129
+ }
2130
+ defs.set(def, (defs.get(def) || 0) + 1);
2131
+ };
2132
+ return {
2133
+ identifiers,
2134
+ graph: buildReducedGraph(state, func, (node, stmt, mayThrow) => {
2135
+ const defs = liveStmts.get(node);
2136
+ if (defs) {
2137
+ liveStmts.delete(node);
2138
+ defs.forEach((count, def) => {
2139
+ if (count > 1) {
2140
+ defs.set(def, count--);
2141
+ return;
2142
+ }
2143
+ const v = liveDefs.get(def);
2144
+ if (!v || !v.has(node)) {
2145
+ throw new Error(`No stmt in liveDef for ${def ? declFullName(def) : "null"}`);
2146
+ }
2147
+ v.delete(node);
2148
+ if (!v.size) {
2149
+ liveDefs.delete(def);
2150
+ }
2151
+ });
2152
+ }
2153
+ switch (node.type) {
2154
+ case "BinaryExpression":
2155
+ case "UnaryExpression":
2156
+ case "SizedArrayExpression":
2157
+ case "ArrayExpression":
2158
+ case "ObjectExpression":
2159
+ case "ThisExpression":
2160
+ case "LogicalExpression":
2161
+ case "ConditionalExpression":
2162
+ case "SequenceExpression":
2163
+ case "ParenthesizedExpression":
2164
+ break;
2165
+ case "Literal":
2166
+ if (refCost(node) > LocalRefCost) {
2167
+ let decl = literals.get(node.value);
2168
+ if (!decl) {
2169
+ decl = node;
2170
+ literals.set(node.value, decl);
2171
+ }
2172
+ return {
2173
+ type: "ref",
2174
+ node,
2175
+ decl: decl,
2176
+ mayThrow,
2177
+ };
2178
+ }
2179
+ break;
2180
+ case "Identifier":
2181
+ identifiers.add(node.name);
2182
+ // fall through
2183
+ case "MemberExpression":
2184
+ {
2185
+ const decl = findDecl(node);
2186
+ if (decl && decl.type === "VariableDeclarator") {
2187
+ const defStmts = (decl.node.kind === "var" && liveDefs.get(null)) ||
2188
+ liveDefs.get(decl);
2189
+ if (defStmts) {
2190
+ break;
2191
+ /*
2192
+ // hold off on this for now. we need to communicate
2193
+ // which defs need to be fixed, which involves yet-another
2194
+ // table.
2195
+
2196
+ if (defStmts.size !== 1) break;
2197
+ const fixable = isFixableStmt([...defStmts][0]);
2198
+ if (fixable === false) break;
2199
+ cost += fixable;
2200
+ */
2201
+ }
2202
+ return {
2203
+ type: "ref",
2204
+ node,
2205
+ decl,
2206
+ mayThrow,
2207
+ };
2208
+ }
2209
+ }
2210
+ break;
2211
+ case "VariableDeclarator": {
2212
+ const decl = findDecl(node.id.type === "BinaryExpression" ? node.id.left : node.id);
2213
+ if (decl) {
2214
+ liveDef(decl, stmt);
2215
+ return {
2216
+ type: "def",
2217
+ node,
2218
+ decl,
2219
+ mayThrow,
2220
+ };
2221
+ }
2222
+ break;
2223
+ }
2224
+ case "AssignmentExpression": {
2225
+ const decl = findDecl(node.left);
2226
+ if (decl) {
2227
+ liveDef(decl, stmt);
2228
+ return {
2229
+ type: "def",
2230
+ node,
2231
+ decl,
2232
+ mayThrow,
2233
+ };
2234
+ }
2235
+ break;
2236
+ }
2237
+ case "UpdateExpression": {
2238
+ const decl = findDecl(node.argument);
2239
+ if (decl) {
2240
+ liveDef(decl, stmt);
2241
+ return {
2242
+ type: "def",
2243
+ node,
2244
+ decl,
2245
+ mayThrow,
2246
+ };
2247
+ }
2248
+ break;
2249
+ }
2250
+ case "NewExpression": {
2251
+ const [, results] = state.lookup(node.callee);
2252
+ const callees = results ? findCalleesForNew(results) : null;
2253
+ liveDef(null, stmt);
2254
+ return { type: "mod", node, mayThrow, callees };
2255
+ }
2256
+ case "CallExpression": {
2257
+ liveDef(null, stmt);
2258
+ const [, results] = state.lookup(node.callee);
2259
+ const callees = results ? findCallees(results) : null;
2260
+ return { type: "mod", node, mayThrow, callees };
2261
+ }
2262
+ default:
2263
+ if (!isExpression(node))
2264
+ break;
2265
+ unhandledExpression(node);
2266
+ }
2267
+ if (mayThrow) {
2268
+ return { type: "exn", node, mayThrow };
2269
+ }
2270
+ return null;
2271
+ }),
2272
+ };
2273
+ }
2274
+ function anticipatedDecls() {
2275
+ return new Map();
2276
+ }
2277
+ function equalSet(a, b) {
2278
+ if (a.size != b.size)
2279
+ return false;
2280
+ for (const item of a) {
2281
+ if (!b.has(item))
2282
+ return false;
2283
+ }
2284
+ return true;
2285
+ }
2286
+ function equalMap(a, b) {
2287
+ if (a.size != b.size)
2288
+ return false;
2289
+ for (const [item, value] of a) {
2290
+ if (b.get(item) !== value)
2291
+ return false;
2292
+ }
2293
+ return true;
2294
+ }
2295
+ function anticipatedState(node, events) {
2296
+ return { ant: events || new Set(), live: true, node, members: new Map() };
2297
+ }
2298
+ function cloneAnticipatedState(as) {
2299
+ return {
2300
+ ant: cloneSet(as.ant),
2301
+ live: as.live,
2302
+ node: as.node,
2303
+ members: new Map(as.members),
2304
+ };
2305
+ }
2306
+ function mergeAnticipatedState(ae, be) {
2307
+ mergeSet(ae.ant, be.ant);
2308
+ be.members.forEach((live, block) => ae.members.set(block, live));
2309
+ if (be.live)
2310
+ ae.live = true;
2311
+ }
2312
+ function cloneAnticipatedDecls(ad) {
2313
+ const copy = anticipatedDecls();
2314
+ for (const [k, v] of ad) {
2315
+ if (!v.isIsolated) {
2316
+ copy.set(k, cloneAnticipatedState(v));
2317
+ }
2318
+ }
2319
+ return copy;
2320
+ }
2321
+ function mergeAnticipatedDecls(a, b) {
2322
+ for (const [k, v] of b) {
2323
+ if (v.isIsolated)
2324
+ continue;
2325
+ const ae = a.get(k);
2326
+ if (ae) {
2327
+ mergeAnticipatedState(ae, v);
2328
+ }
2329
+ else {
2330
+ a.set(k, cloneAnticipatedState(v));
2331
+ }
2332
+ }
2333
+ }
2334
+ function equalStates(a, b) {
2335
+ if (a.size !== b.size)
2336
+ return false;
2337
+ for (const [k, ae] of a) {
2338
+ const be = b.get(k);
2339
+ if (!be ||
2340
+ be.live != ae.live ||
2341
+ be.isIsolated != ae.isIsolated ||
2342
+ !equalSet(ae.ant, be.ant) ||
2343
+ !equalMap(ae.members, be.members)) {
2344
+ return false;
2345
+ }
2346
+ }
2347
+ return true;
2348
+ }
2349
+ const LocalRefCost = 2;
2350
+ function refCost(node) {
2351
+ if (node.type === "Literal") {
2352
+ switch (typeof node.value) {
2353
+ case "string":
2354
+ return 5;
2355
+ case "bigint":
2356
+ case "number":
2357
+ return 5;
2358
+ case "boolean":
2359
+ return 2;
2360
+ default:
2361
+ if (node.value === null) {
2362
+ return 2;
2363
+ }
2364
+ return 0;
2365
+ }
2366
+ }
2367
+ // A read from a non-local identifier takes 8 bytes
2368
+ let cost = 8;
2369
+ if (node.type === "Identifier")
2370
+ return cost;
2371
+ while (true) {
2372
+ const next = node.object;
2373
+ if (next.type != "MemberExpression") {
2374
+ if (next.type != "ThisExpression") {
2375
+ cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
2376
+ }
2377
+ return cost;
2378
+ }
2379
+ node = next;
2380
+ cost += 6;
2381
+ }
2382
+ }
2383
+ function defCost(node) {
2384
+ return refCost(node) + 2;
2385
+ }
2386
+ function candidateBoundary(candState) {
2387
+ const boundary = new Set();
2388
+ candState.members.forEach((live, block) => {
2389
+ if (live && block !== candState.head) {
2390
+ if (block.preds) {
2391
+ block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
2392
+ }
2393
+ }
2394
+ });
2395
+ if (candState.live) {
2396
+ if (!candState.head) {
2397
+ throw new Error(`Missing head`);
2398
+ }
2399
+ boundary.add(candState.head);
2400
+ }
2401
+ return boundary;
2402
+ }
2403
+ function candidateCost(candState) {
2404
+ let cost = 0;
2405
+ candState.ant.forEach((event) => {
2406
+ if (event.type === "ref") {
2407
+ cost -= refCost(candState.node) - LocalRefCost;
2408
+ }
2409
+ else {
2410
+ cost += defCost(candState.node);
2411
+ }
2412
+ });
2413
+ const boundarySize = candidateBoundary(candState).size;
2414
+ cost += defCost(candState.node) * boundarySize;
2415
+ return cost;
2416
+ }
2417
+ function computeAttributes(state, head) {
2418
+ const order = getPostOrder(head);
2419
+ order.forEach((block, i) => {
2420
+ block.order = i;
2421
+ });
2422
+ if (logging) {
2423
+ order.forEach((block) => {
2424
+ console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
2425
+ .map((block) => block.order)
2426
+ .join(", ")}`);
2427
+ if (block.events) {
2428
+ block.events.forEach((event) => event.type !== "exn" &&
2429
+ console.log(` ${event.type}: ${event.decl ? declFullName(event.decl) : "??"}`));
2430
+ }
2431
+ console.log(`Succs: ${(block.succs || [])
2432
+ .map((block) => block.order)
2433
+ .join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
2434
+ });
2435
+ }
2436
+ const enqueued = new Set();
2437
+ const queue = new PriorityQueue((b, a) => (a.order || 0) - (b.order || 0));
2438
+ const enqueue = (block) => {
2439
+ if (!enqueued.has(block)) {
2440
+ enqueued.add(block);
2441
+ queue.enq(block);
2442
+ }
2443
+ };
2444
+ const dequeue = () => {
2445
+ const block = queue.deq();
2446
+ enqueued.delete(block);
2447
+ return block;
2448
+ };
2449
+ const blockStates = [];
2450
+ /*
2451
+ Algorithm
2452
+ =========
2453
+
2454
+ Process blocks in post-order, and the events in reverse
2455
+ order to collect the AnticipatedState at the start of each
2456
+ Block.
2457
+
2458
+ Then for each EventDecl find the best starting block.
2459
+ */
2460
+ const modMap = new Map();
2461
+ const getMod = (event, decl, id) => {
2462
+ if (id.type !== "Identifier" && id.type !== "MemberExpression") {
2463
+ throw new Error("Trying to modify a non-variable");
2464
+ }
2465
+ let eventMap = modMap.get(event);
2466
+ if (!eventMap) {
2467
+ modMap.set(event, (eventMap = new Map()));
2468
+ }
2469
+ let result = eventMap.get(decl);
2470
+ if (!result) {
2471
+ result = {
2472
+ type: "mod",
2473
+ node: event.node,
2474
+ decl,
2475
+ id,
2476
+ mayThrow: event.mayThrow,
2477
+ };
2478
+ eventMap.set(decl, result);
2479
+ }
2480
+ return result;
2481
+ };
2482
+ order.forEach((block) => enqueue(block));
2483
+ while (queue.size()) {
2484
+ const top = dequeue();
2485
+ if (top.order === undefined) {
2486
+ throw new Error(`Unreachable block was visited!`);
2487
+ }
2488
+ const curState = (top.succs &&
2489
+ top.succs.reduce((blockState, succ) => {
2490
+ const succState = blockStates[succ.order];
2491
+ if (succState) {
2492
+ if (!blockState) {
2493
+ blockState = cloneAnticipatedDecls(succState);
2494
+ }
2495
+ else {
2496
+ mergeAnticipatedDecls(blockState, succState);
2497
+ }
2498
+ }
2499
+ return blockState;
2500
+ }, null)) ||
2501
+ anticipatedDecls();
2502
+ if (top.events) {
2503
+ for (let i = top.events.length; i--;) {
2504
+ const event = top.events[i];
2505
+ if (event.mayThrow && top.exsucc) {
2506
+ const succState = blockStates[top.exsucc.order];
2507
+ if (succState) {
2508
+ mergeAnticipatedDecls(curState, succState);
2509
+ }
2510
+ }
2511
+ switch (event.type) {
2512
+ case "ref": {
2513
+ let candidates = curState.get(event.decl);
2514
+ if (!candidates) {
2515
+ candidates = anticipatedState(event.node);
2516
+ curState.set(event.decl, candidates);
2517
+ }
2518
+ candidates.ant.add(event);
2519
+ candidates.live = true;
2520
+ break;
2521
+ }
2522
+ case "mod": {
2523
+ curState.forEach((candidates, decl) => {
2524
+ if (decl.type === "VariableDeclarator" &&
2525
+ decl.node.kind === "var" &&
2526
+ candidates.live &&
2527
+ (!event.callees ||
2528
+ event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
2529
+ candidates.ant.add(getMod(event, decl, candidates.node));
2530
+ candidates.live = false;
2531
+ }
2532
+ });
2533
+ break;
2534
+ }
2535
+ case "def": {
2536
+ let candidates = curState.get(event.decl);
2537
+ const isUpdate = event.node.type === "UpdateExpression" ||
2538
+ (event.node.type === "AssignmentExpression" &&
2539
+ event.node.operator !== "=");
2540
+ if (!candidates) {
2541
+ const target = event.node.type === "AssignmentExpression"
2542
+ ? event.node.left
2543
+ : event.node.type === "UpdateExpression"
2544
+ ? event.node.argument
2545
+ : event.node.id.type === "BinaryExpression"
2546
+ ? event.node.id.left
2547
+ : event.node.id;
2548
+ candidates = anticipatedState(target);
2549
+ curState.set(event.decl, candidates);
2550
+ }
2551
+ if (isUpdate || candidates.live) {
2552
+ candidates.ant.add(event);
2553
+ }
2554
+ candidates.live = isUpdate;
2555
+ break;
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ curState.forEach((antState) => {
2561
+ antState.head = top;
2562
+ antState.members.set(top, antState.live);
2563
+ if (!antState.live && candidateBoundary(antState).size === 0) {
2564
+ // we found a group that's isolated from the rest
2565
+ // of the function. Don't merge it with earlier
2566
+ // refs and defs, because we can take it or leave
2567
+ // it based on its own cost.
2568
+ antState.isIsolated = true;
2569
+ }
2570
+ });
2571
+ const oldState = blockStates[top.order];
2572
+ if (oldState && equalStates(oldState, curState)) {
2573
+ continue;
2574
+ }
2575
+ blockStates[top.order] = curState;
2576
+ if (logging) {
2577
+ console.log(`Updated block ${top.order}`);
2578
+ logAntDecls(curState);
2579
+ }
2580
+ if (top.preds) {
2581
+ top.preds.forEach((pred) => enqueue(pred));
2582
+ }
2583
+ }
2584
+ const candidateDecls = anticipatedDecls();
2585
+ blockStates.forEach((blockState, i) => {
2586
+ blockState &&
2587
+ blockState.forEach((events, decl) => {
2588
+ const cost = candidateCost(events);
2589
+ if (cost >= 0)
2590
+ return;
2591
+ const existing = candidateDecls.get(decl);
2592
+ if (!existing ||
2593
+ existing.isIsolated ||
2594
+ candidateCost(existing) > cost) {
2595
+ const boundary = candidateBoundary(events);
2596
+ if (!Array.from(boundary).every((block) => {
2597
+ if (block !== events.head && block.events) {
2598
+ if (events.node.type === "Literal") {
2599
+ return false;
2600
+ }
2601
+ let i = block.events.length;
2602
+ while (i--) {
2603
+ const event = block.events[i];
2604
+ if (event.type === "def" || event.type === "mod") {
2605
+ events.ant.add({
2606
+ type: "mod",
2607
+ node: event.node,
2608
+ decl,
2609
+ id: events.node,
2610
+ mayThrow: false,
2611
+ });
2612
+ events.members.set(block, false);
2613
+ return true;
2614
+ }
2615
+ }
2616
+ }
2617
+ const node = block.node;
2618
+ if (!node)
2619
+ return false;
2620
+ events.ant.add({
2621
+ type: "mod",
2622
+ node: node.type === "FunctionDeclaration" ? node.body : node,
2623
+ before: true,
2624
+ decl,
2625
+ id: events.node,
2626
+ mayThrow: false,
2627
+ });
2628
+ events.members.set(block, false);
2629
+ return true;
2630
+ })) {
2631
+ return;
2632
+ }
2633
+ events.live = false;
2634
+ if (existing && existing.isIsolated) {
2635
+ delete existing.isIsolated;
2636
+ mergeAnticipatedState(events, existing);
2637
+ }
2638
+ else if (candidateCost(events) != cost) {
2639
+ throw new Error(`cost of block ${i} changed`);
2640
+ }
2641
+ candidateDecls.set(decl, events);
2642
+ }
2643
+ });
2644
+ });
2645
+ if (candidateDecls.size) {
2646
+ return candidateDecls;
2647
+ }
2648
+ return null;
2649
+ }
2650
+ /*
2651
+ * Determine the cost of fixing a def under a statement.
2652
+ *
2653
+ * eg:
2654
+ *
2655
+ * if (foo()) {
2656
+ * bar(X.y);
2657
+ * } else {
2658
+ * baz(X.y);
2659
+ * }
2660
+ *
2661
+ * Here, we could pull out X.y as a local, but if foo might modify
2662
+ * X.y, we have nowhere to insert the temporary. But we can rewrite
2663
+ * it as:
2664
+ *
2665
+ * var tmp = foo();
2666
+ * if (tmp) {
2667
+ * bar(X.y);
2668
+ * } else {
2669
+ * baz(X.y);
2670
+ * }
2671
+ *
2672
+ * and now we can insert a temporary before the if, but it costs
2673
+ * 4 bytes to do so.
2674
+ *
2675
+ * We can do the same for switch statements unless (ugh!)
2676
+ * the cases might modify the decl too.
2677
+ *
2678
+ * eg
2679
+ *
2680
+ * switch (foo()) {
2681
+ * case bar(): ...
2682
+ * }
2683
+ *
2684
+ */
2685
+ function _isFixableStmt(node) {
2686
+ switch (node.type) {
2687
+ case "IfStatement":
2688
+ return 4;
2689
+ case "SwitchStatement":
2690
+ if (node.cases.every((c) => !c.test ||
2691
+ c.test.type === "Literal" ||
2692
+ c.test.type === "Identifier" ||
2693
+ c.test.type === "InstanceOfCase" ||
2694
+ (c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
2695
+ return 4;
2696
+ }
2697
+ break;
2698
+ }
2699
+ return false;
2700
+ }
2701
+ function applyReplacements(func, nodeMap, declMap) {
2702
+ const ident = (name, node) => {
2703
+ return withLoc({ type: "Identifier", name }, node);
2704
+ };
2705
+ const pendingMap = new Map();
2706
+ const stmtStack = [func];
2707
+ traverseAst(func, (node) => {
2708
+ if (isStatement(node)) {
2709
+ stmtStack.push(node);
2710
+ }
2711
+ }, (node) => {
2712
+ const stmt = stmtStack[stmtStack.length - 1];
2713
+ if (stmt === node)
2714
+ stmtStack.pop();
2715
+ const events = nodeMap.get(node);
2716
+ if (events) {
2717
+ const ret = events.reduce((ret, event) => {
2718
+ if (event.type === "ref") {
2719
+ if (ret) {
2720
+ throw new Error(`ref found when there was already a replacement for this node`);
2721
+ }
2722
+ if (node.type !== "Identifier" &&
2723
+ node.type !== "MemberExpression" &&
2724
+ node.type !== "Literal") {
2725
+ throw new Error(`Ref found, but wrong type of node: ${node.type}`);
2726
+ }
2727
+ const name = declMap.get(event.decl);
2728
+ if (!name) {
2729
+ throw new Error(`No replacement found for "${formatAst(node)}"`);
2730
+ }
2731
+ return ident(name, node);
2732
+ }
2733
+ if (event.type === "def") {
2734
+ if (ret) {
2735
+ throw new Error(`def found when there was already a replacement for this node`);
2736
+ }
2737
+ if (node.type !== "AssignmentExpression" &&
2738
+ node.type !== "UpdateExpression") {
2739
+ throw new Error(`Def found, but wrong type of node: ${node.type}`);
2740
+ }
2741
+ const target = node.type === "AssignmentExpression"
2742
+ ? node.left
2743
+ : node.argument;
2744
+ const name = declMap.get(event.decl);
2745
+ if (!name) {
2746
+ throw new Error(`No replacement found for "${formatAst(target)}"`);
2747
+ }
2748
+ const id = ident(name, target);
2749
+ const assign = withLoc({
2750
+ type: "AssignmentExpression",
2751
+ left: target,
2752
+ right: { ...id },
2753
+ operator: "=",
2754
+ }, node);
2755
+ if (node.type === "AssignmentExpression") {
2756
+ node.left = id;
2757
+ }
2758
+ else {
2759
+ node.argument = id;
2760
+ }
2761
+ return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
2762
+ }
2763
+ if (event.type === "mod") {
2764
+ if (!event.decl) {
2765
+ throw new Error(`Unexpected null decl on mod event`);
2766
+ }
2767
+ let pending = pendingMap.get(stmt);
2768
+ if (!pending) {
2769
+ pendingMap.set(stmt, (pending = new Set()));
2770
+ }
2771
+ pending.add(event);
2772
+ }
2773
+ else {
2774
+ throw new Error(`Unexpected ${event.type} found`);
2775
+ }
2776
+ return ret;
2777
+ }, null);
2778
+ if (ret) {
2779
+ return ret;
2780
+ }
2781
+ }
2782
+ const pending = pendingMap.get(node);
2783
+ if (node.type === "SequenceExpression") {
2784
+ if (pending) {
2785
+ throw new Error(`Unexpected pending list at SequenceExpression`);
2786
+ }
2787
+ for (let i = node.expressions.length; i--;) {
2788
+ const ni = node.expressions[i];
2789
+ if (ni.type === "SequenceExpression") {
2790
+ node.expressions.splice(i, 1, ...ni.expressions);
2791
+ }
2792
+ }
2793
+ }
2794
+ const applyPending = (results, locNode) => {
2795
+ const target = results.length === 1 && results[0].type === "BlockStatement"
2796
+ ? results[0]
2797
+ : null;
2798
+ pendingMap.delete(node);
2799
+ pending.forEach((event) => {
2800
+ const decl = event.decl;
2801
+ const name = declMap.get(decl);
2802
+ if (!name) {
2803
+ throw new Error(`No replacement found for "${declFullName(decl)}"`);
2804
+ }
2805
+ if (!event.id) {
2806
+ throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
2807
+ }
2808
+ const rhs = withLocDeep(event.id, locNode, locNode);
2809
+ rhs.end = rhs.start;
2810
+ if (rhs.loc) {
2811
+ rhs.loc.end = rhs.loc.start;
2812
+ }
2813
+ const insertion = withLoc({
2814
+ type: "ExpressionStatement",
2815
+ expression: withLoc({
2816
+ type: "AssignmentExpression",
2817
+ left: ident(name, rhs),
2818
+ right: rhs,
2819
+ operator: "=",
2820
+ }, rhs),
2821
+ }, rhs);
2822
+ if (event.type === "mod" && event.before) {
2823
+ if (target) {
2824
+ target.body.unshift(insertion);
2825
+ }
2826
+ else {
2827
+ results.unshift(insertion);
2828
+ }
2829
+ }
2830
+ else {
2831
+ results.push(insertion);
2832
+ }
2833
+ });
2834
+ return results.length === 1 ? null : results;
2835
+ };
2836
+ if (node.type === "ExpressionStatement" &&
2837
+ node.expression.type === "SequenceExpression") {
2838
+ const results = [];
2839
+ node.expression.expressions.forEach((expression) => {
2840
+ results.push({ ...node, expression });
2841
+ });
2842
+ if (!pending) {
2843
+ return results;
2844
+ }
2845
+ return applyPending(results, node);
2846
+ }
2847
+ if (pending) {
2848
+ return applyPending([node], node);
2849
+ }
2850
+ return null;
2851
+ });
2852
+ }
2853
+
2854
+ ;// CONCATENATED MODULE: ./src/unused-exprs.ts
2855
+
2856
+
2857
+
2858
+ function unused_exprs_cleanupUnusedVars(state, node) {
2859
+ const [parent] = state.stack.slice(-1);
2860
+ if (parent.node !== node) {
2861
+ return;
2862
+ }
2863
+ if (parent.type != "BlockStatement") {
2864
+ throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
2865
+ }
2866
+ if (parent.decls) {
2867
+ let toRemove = null;
2868
+ Object.values(parent.decls).forEach((decls) => {
2869
+ if (decls.length === 1 &&
2870
+ decls[0].type === "VariableDeclarator" &&
2871
+ !decls[0].used) {
2872
+ if (!toRemove)
2873
+ toRemove = {};
2874
+ toRemove[decls[0].name] = decls[0];
2875
+ }
2876
+ });
2877
+ if (toRemove) {
2878
+ const varDeclarations = new Map();
2879
+ traverseAst(node, null, (node) => {
2880
+ switch (node.type) {
2881
+ case "VariableDeclaration": {
2882
+ node.declarations.forEach((decl, i) => {
2883
+ const name = variableDeclarationName(decl.id);
2884
+ if (hasProperty(toRemove, name)) {
2885
+ const indices = varDeclarations.get(node);
2886
+ if (indices) {
2887
+ indices.push(i);
2888
+ }
2889
+ else {
2890
+ varDeclarations.set(node, [i]);
2891
+ }
2892
+ }
2893
+ });
2894
+ break;
2895
+ }
2896
+ case "ExpressionStatement":
2897
+ if (node.expression.type === "AssignmentExpression") {
2898
+ if (node.expression.left.type === "Identifier" &&
2899
+ hasProperty(toRemove, node.expression.left.name)) {
2900
+ return unused(node.expression.right);
2901
+ }
2902
+ }
2903
+ else if (node.expression.type === "UpdateExpression" &&
2904
+ node.expression.argument.type === "Identifier" &&
2905
+ hasProperty(toRemove, node.expression.argument.name)) {
2906
+ return false;
2907
+ }
2908
+ break;
2909
+ case "SequenceExpression": {
2910
+ for (let i = node.expressions.length; i--;) {
2911
+ const expr = node.expressions[i];
2912
+ if (expr.type === "AssignmentExpression") {
2913
+ if (expr.left.type === "Identifier" &&
2914
+ hasProperty(toRemove, expr.left.name)) {
2915
+ const rep = unused(expr.right);
2916
+ if (!rep.length) {
2917
+ node.expressions.splice(i, 1);
2918
+ }
2919
+ else {
2920
+ // Sequence expressions can only be assignments
2921
+ // or update expressions. Even calls aren't allowed
2922
+ toRemove[expr.left.name] = null;
2923
+ expr.operator = "=";
2924
+ }
2925
+ }
2926
+ }
2927
+ else if (expr.type === "UpdateExpression" &&
2928
+ expr.argument.type === "Identifier" &&
2929
+ hasProperty(toRemove, expr.argument.name)) {
2930
+ node.expressions.splice(i, 1);
2931
+ }
2932
+ }
2933
+ break;
2934
+ }
2935
+ }
2936
+ return null;
2937
+ });
2938
+ varDeclarations.forEach((indices, decl) => {
2939
+ let index = -1;
2940
+ for (let ii = indices.length, j = decl.declarations.length; ii--;) {
2941
+ const i = indices[ii];
2942
+ const vdecl = decl.declarations[i];
2943
+ const name = variableDeclarationName(vdecl.id);
2944
+ if (hasProperty(toRemove, name)) {
2945
+ const rep = vdecl.init ? unused(vdecl.init) : [];
2946
+ if (rep.length) {
2947
+ if (parent.node.type === "ForStatement") {
2948
+ // declarations whose inits have side effects
2949
+ // can't be deleted from for statements.
2950
+ continue;
2951
+ }
2952
+ if (index < 0) {
2953
+ index = parent.node.body.findIndex((s) => s === decl);
2954
+ if (index < 0) {
2955
+ throw new Error(`Failed to find variable declaration for ${variableDeclarationName(vdecl.id)}`);
2956
+ }
2957
+ }
2958
+ if (j > i + 1) {
2959
+ const tail = {
2960
+ ...decl,
2961
+ declarations: decl.declarations.slice(i + 1, j),
2962
+ };
2963
+ if (decl.loc && vdecl.loc) {
2964
+ tail.loc = { ...decl.loc, start: vdecl.loc.end };
2965
+ tail.start = vdecl.end;
2966
+ }
2967
+ rep.push(tail);
2968
+ }
2969
+ if (decl.loc && vdecl.loc) {
2970
+ decl.loc = { ...decl.loc, end: vdecl.loc.start };
2971
+ decl.end = vdecl.start;
2972
+ }
2973
+ decl.declarations.splice(i);
2974
+ parent.node.body.splice(index + 1, 0, ...rep);
2975
+ j = i;
2976
+ continue;
2977
+ }
2978
+ if (toRemove[name]) {
2979
+ j--;
2980
+ decl.declarations.splice(i, 1);
2981
+ if (i === j && decl.loc && vdecl.loc) {
2982
+ decl.loc = { ...decl.loc, end: vdecl.loc.start };
2983
+ decl.end = vdecl.start;
2984
+ }
2985
+ }
2986
+ else {
2987
+ delete vdecl.init;
2988
+ }
2989
+ }
2990
+ }
2991
+ });
2992
+ }
2993
+ }
803
2994
  }
804
2995
 
805
- ;// CONCATENATED MODULE: external "./util.cjs"
806
- const external_util_cjs_namespaceObject = require("./util.cjs");
807
2996
  ;// CONCATENATED MODULE: ./src/visitor.ts
808
2997
 
809
2998
  function visitor_visitReferences(state, ast, name, defn, callback) {
@@ -868,14 +3057,16 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
868
3057
  return checkResults(state.lookup(node), node);
869
3058
  }
870
3059
  break;
871
- case "MemberExpression":
872
- if (!node.computed && node.property.type === "Identifier") {
873
- if (!name || node.property.name === name) {
3060
+ case "MemberExpression": {
3061
+ const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
3062
+ if (property) {
3063
+ if (!name || property.name === name) {
874
3064
  return checkResults(state.lookup(node), node) || ["object"];
875
3065
  }
876
3066
  return ["object"];
877
3067
  }
878
3068
  break;
3069
+ }
879
3070
  case "MethodDefinition": {
880
3071
  if (!state.inType) {
881
3072
  throw new Error("Method definition outside of type!");
@@ -884,7 +3075,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
884
3075
  node.params.forEach((param) => {
885
3076
  if (param.type == "BinaryExpression") {
886
3077
  state.traverse(param.right);
887
- state.inType = true;
888
3078
  }
889
3079
  });
890
3080
  }
@@ -906,6 +3096,10 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
906
3096
 
907
3097
 
908
3098
 
3099
+
3100
+
3101
+
3102
+
909
3103
  function collectClassInfo(state) {
910
3104
  const toybox = state.stack[0].decls["Toybox"][0];
911
3105
  const lang = toybox.decls["Lang"][0];
@@ -965,8 +3159,7 @@ function collectClassInfo(state) {
965
3159
  c.decls &&
966
3160
  Object.values(c.decls).forEach((funcs) => {
967
3161
  funcs.forEach((f) => {
968
- if (isStateNode(f) &&
969
- f.type === "FunctionDeclaration" &&
3162
+ if (f.type === "FunctionDeclaration" &&
970
3163
  hasProperty(cls.decls, f.name)) {
971
3164
  f.node.hasOverride = true;
972
3165
  }
@@ -979,6 +3172,15 @@ function collectClassInfo(state) {
979
3172
  state.allClasses.forEach((elm) => {
980
3173
  if (elm.superClass)
981
3174
  markOverrides(elm, elm.superClass);
3175
+ if (elm.hasInvoke && elm.decls) {
3176
+ Object.values(elm.decls).forEach((funcs) => {
3177
+ funcs.forEach((f) => {
3178
+ if (f.type === "FunctionDeclaration" && !f.isStatic) {
3179
+ markInvokeClassMethod(f);
3180
+ }
3181
+ });
3182
+ });
3183
+ }
982
3184
  });
983
3185
  }
984
3186
  function getFileSources(fnMap) {
@@ -1018,7 +3220,7 @@ async function analyze(fnMap, barrelList, config) {
1018
3220
  const preState = {
1019
3221
  fnMap,
1020
3222
  config,
1021
- allFunctions: [],
3223
+ allFunctions: {},
1022
3224
  allClasses: [],
1023
3225
  shouldExclude(node) {
1024
3226
  if ("attrs" in node &&
@@ -1048,11 +3250,6 @@ async function analyze(fnMap, barrelList, config) {
1048
3250
  pre(node, state) {
1049
3251
  switch (node.type) {
1050
3252
  case "FunctionDeclaration":
1051
- if (markApi) {
1052
- node.body = null;
1053
- break;
1054
- }
1055
- // falls through
1056
3253
  case "ModuleDeclaration":
1057
3254
  case "ClassDeclaration": {
1058
3255
  const [scope] = state.stack.slice(-1);
@@ -1063,7 +3260,18 @@ async function analyze(fnMap, barrelList, config) {
1063
3260
  (scope.node.attrs &&
1064
3261
  scope.node.attrs.access &&
1065
3262
  scope.node.attrs.access.includes("static"));
1066
- state.allFunctions.push(scope);
3263
+ if (markApi) {
3264
+ node.body = null;
3265
+ scope.info = getApiFunctionInfo(scope);
3266
+ delete scope.stack;
3267
+ }
3268
+ const allFuncs = state.allFunctions;
3269
+ if (!hasProperty(allFuncs, scope.name)) {
3270
+ allFuncs[scope.name] = [scope];
3271
+ }
3272
+ else {
3273
+ allFuncs[scope.name].push(scope);
3274
+ }
1067
3275
  }
1068
3276
  else if (scope.type === "ClassDeclaration") {
1069
3277
  state.allClasses.push(scope);
@@ -1088,7 +3296,7 @@ async function analyze(fnMap, barrelList, config) {
1088
3296
  value.hasTests = hasTests;
1089
3297
  });
1090
3298
  delete state.shouldExclude;
1091
- delete state.post;
3299
+ delete state.pre;
1092
3300
  collectClassInfo(state);
1093
3301
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
1094
3302
  ? config?.checkInvalidSymbols || "WARNING"
@@ -1111,6 +3319,8 @@ async function analyze(fnMap, barrelList, config) {
1111
3319
  });
1112
3320
  });
1113
3321
  }
3322
+ state.exposed = state.nextExposed;
3323
+ state.nextExposed = {};
1114
3324
  return state;
1115
3325
  }
1116
3326
  function compareLiteralLike(a, b) {
@@ -1156,15 +3366,12 @@ function getLiteralNode(node) {
1156
3366
  if (node.argument.type != "Literal")
1157
3367
  return null;
1158
3368
  switch (node.operator) {
1159
- case "-":
1160
- if (typeof node.argument.value == "number") {
1161
- return {
1162
- ...node.argument,
1163
- value: -node.argument.value,
1164
- raw: "-" + node.argument.value,
1165
- enumType: node.enumType,
1166
- };
3369
+ case "-": {
3370
+ const [arg, type] = getNodeValue(node.argument);
3371
+ if (type === "Number" || type === "Long") {
3372
+ return replacementLiteral(arg, -arg.value, type);
1167
3373
  }
3374
+ }
1168
3375
  }
1169
3376
  }
1170
3377
  return null;
@@ -1183,31 +3390,114 @@ function getNodeValue(node) {
1183
3390
  if (node.type != "Literal") {
1184
3391
  return [null, null];
1185
3392
  }
1186
- let type = node.value === null ? "Null" : typeof node.value;
3393
+ if (node.value === null) {
3394
+ return [node, "Null"];
3395
+ }
3396
+ const type = typeof node.value;
1187
3397
  if (type === "number") {
1188
- const match = node.raw && LiteralIntegerRe.exec(node.raw);
3398
+ const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
1189
3399
  if (match) {
1190
- type = match[2] == "l" ? "Long" : "Number";
1191
- }
1192
- else if (node.raw && node.raw.endsWith("d")) {
1193
- type = "Double";
3400
+ return match[2] === "l" || match[2] === "L"
3401
+ ? [node, "Long"]
3402
+ : [node, "Number"];
1194
3403
  }
1195
- else {
1196
- type = "Float";
3404
+ return [node, node.raw.endsWith("d") ? "Double" : "Float"];
3405
+ }
3406
+ if (type === "bigint") {
3407
+ return [node, "Long"];
3408
+ }
3409
+ if (type === "string") {
3410
+ return [node, "String"];
3411
+ }
3412
+ if (type === "boolean") {
3413
+ return [node, "Boolean"];
3414
+ }
3415
+ throw new Error(`Literal has unknown type '${type}'`);
3416
+ }
3417
+ function fullTypeName(state, tsp) {
3418
+ if (typeof tsp.name === "string") {
3419
+ return tsp.name;
3420
+ }
3421
+ const [, results] = state.lookupType(tsp.name);
3422
+ if (results && results.length === 1 && results[0].results.length === 1) {
3423
+ const result = results[0].results[0];
3424
+ if (isStateNode(result)) {
3425
+ return result.fullName;
1197
3426
  }
1198
3427
  }
1199
- else if (type === "string") {
1200
- type = "String";
3428
+ return null;
3429
+ }
3430
+ function isBooleanExpression(state, node) {
3431
+ switch (node.type) {
3432
+ case "Literal":
3433
+ return typeof node.value === "boolean";
3434
+ case "BinaryExpression":
3435
+ switch (node.operator) {
3436
+ case "==":
3437
+ case "!=":
3438
+ case "<=":
3439
+ case ">=":
3440
+ case "<":
3441
+ case ">":
3442
+ return true;
3443
+ case "as":
3444
+ return node.right.ts.length === 1 &&
3445
+ node.right.ts[0].type === "TypeSpecPart" &&
3446
+ node.right.ts[0].name &&
3447
+ fullTypeName(state, node.right.ts[0]) === "$.Toybox.Lang.Boolean"
3448
+ ? true
3449
+ : false;
3450
+ }
3451
+ return false;
3452
+ case "LogicalExpression":
3453
+ return (isBooleanExpression(state, node.left) &&
3454
+ isBooleanExpression(state, node.right));
3455
+ case "UnaryExpression":
3456
+ return node.operator === "!" && isBooleanExpression(state, node.argument);
1201
3457
  }
1202
- else if (type === "boolean") {
3458
+ return false;
3459
+ }
3460
+ function replacementLiteral(arg, value, type) {
3461
+ if (typeof value === "boolean") {
1203
3462
  type = "Boolean";
1204
3463
  }
1205
- else {
1206
- type = "Unknown";
3464
+ else if (type === "Number") {
3465
+ value = Number(BigInt.asIntN(32, BigInt(value)));
3466
+ }
3467
+ else if (type === "Long") {
3468
+ value = BigInt.asIntN(64, BigInt(value));
1207
3469
  }
1208
- return [node, type];
3470
+ return {
3471
+ ...arg,
3472
+ value,
3473
+ raw: value.toString() + (type === "Long" ? "l" : ""),
3474
+ };
1209
3475
  }
1210
- function optimizeNode(node) {
3476
+ const operators = {
3477
+ "+": (left, right) => left + right,
3478
+ "-": (left, right) => left - right,
3479
+ "*": (left, right) => left * right,
3480
+ "/": (left, right) => left / right,
3481
+ "%": (left, right) => left % right,
3482
+ "&": (left, right) => left & right,
3483
+ "|": (left, right) => left | right,
3484
+ "^": (left, right) => left ^ right,
3485
+ "<<": (left, right) => left << (right & 127n),
3486
+ ">>": (left, right) => left >> (right & 127n),
3487
+ "==": (left, right) =>
3488
+ // two string literals will compare unequal, becuase string
3489
+ // equality is object equality.
3490
+ typeof left === "string" ? false : left === right,
3491
+ "!=": (left, right) => typeof left === "string" ? true : left !== right,
3492
+ "<=": (left, right) => left <= right,
3493
+ ">=": (left, right) => left >= right,
3494
+ "<": (left, right) => left < right,
3495
+ ">": (left, right) => left > right,
3496
+ as: null,
3497
+ instanceof: null,
3498
+ has: null,
3499
+ };
3500
+ function optimizeNode(state, node) {
1211
3501
  switch (node.type) {
1212
3502
  case "UnaryExpression": {
1213
3503
  const [arg, type] = getNodeValue(node.argument);
@@ -1221,29 +3511,17 @@ function optimizeNode(node) {
1221
3511
  break;
1222
3512
  case "-":
1223
3513
  if (type === "Number" || type === "Long") {
1224
- return {
1225
- ...arg,
1226
- value: -arg.value,
1227
- raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
1228
- };
3514
+ return replacementLiteral(arg, -arg.value, type);
1229
3515
  }
1230
3516
  break;
1231
3517
  case "!":
1232
3518
  case "~":
1233
3519
  {
1234
- let value;
1235
3520
  if (type === "Number" || type === "Long") {
1236
- value = -arg.value - 1;
3521
+ return replacementLiteral(arg, ~BigInt(arg.value), type);
1237
3522
  }
1238
- else if (type === "Boolean" && node.operator == "!") {
1239
- value = !arg.value;
1240
- }
1241
- if (value !== undefined) {
1242
- return {
1243
- ...arg,
1244
- value,
1245
- raw: value.toString() + (type === "Long" ? "l" : ""),
1246
- };
3523
+ if (type === "Boolean" && node.operator == "!") {
3524
+ return replacementLiteral(arg, !arg.value, type);
1247
3525
  }
1248
3526
  }
1249
3527
  break;
@@ -1251,56 +3529,81 @@ function optimizeNode(node) {
1251
3529
  break;
1252
3530
  }
1253
3531
  case "BinaryExpression": {
1254
- const operators = {
1255
- "+": (left, right) => left + right,
1256
- "-": (left, right) => left - right,
1257
- "*": (left, right) => left * right,
1258
- "/": (left, right) => Math.trunc(left / right),
1259
- "%": (left, right) => left % right,
1260
- "&": (left, right, type) => type === "Number" ? left & right : null,
1261
- "|": (left, right, type) => type === "Number" ? left | right : null,
1262
- "<<": (left, right, type) => type === "Number" ? left << right : null,
1263
- ">>": (left, right, type) => type === "Number" ? left >> right : null,
1264
- };
1265
3532
  const op = operators[node.operator];
1266
3533
  if (op) {
1267
3534
  const [left, left_type] = getNodeValue(node.left);
1268
3535
  const [right, right_type] = getNodeValue(node.right);
1269
3536
  if (!left || !right)
1270
3537
  break;
1271
- if (left_type != right_type ||
1272
- (left_type != "Number" && left_type != "Long")) {
1273
- break;
3538
+ let value = null;
3539
+ let type;
3540
+ if ((left_type != "Number" && left_type != "Long") ||
3541
+ left_type != right_type) {
3542
+ if (node.operator !== "==" && node.operator !== "!=") {
3543
+ break;
3544
+ }
3545
+ value = operators[node.operator](left.value, right.value);
3546
+ type = "Boolean";
3547
+ }
3548
+ else {
3549
+ type = left_type;
3550
+ value = op(BigInt(left.value), BigInt(right.value));
1274
3551
  }
1275
- const value = op(left.value, right.value, left_type);
1276
3552
  if (value === null)
1277
3553
  break;
1278
- return {
1279
- ...left,
1280
- value,
1281
- raw: value.toString() + (left_type === "Long" ? "l" : ""),
1282
- };
3554
+ return replacementLiteral(left, value, type);
3555
+ }
3556
+ break;
3557
+ }
3558
+ case "LogicalExpression": {
3559
+ const [left, left_type] = getNodeValue(node.left);
3560
+ if (!left)
3561
+ break;
3562
+ const falsy = left.value === false ||
3563
+ left.value === null ||
3564
+ ((left_type === "Number" || left_type === "Long") &&
3565
+ (left.value === 0 || left.value === 0n));
3566
+ if (falsy === (node.operator === "&&")) {
3567
+ return left;
3568
+ }
3569
+ if (left_type !== "Boolean" &&
3570
+ left_type !== "Number" &&
3571
+ left_type !== "Long") {
3572
+ break;
3573
+ }
3574
+ const [right, right_type] = getNodeValue(node.right);
3575
+ if (right && right_type === left_type) {
3576
+ if (left_type === "Boolean" || node.operator === "||") {
3577
+ return right;
3578
+ }
3579
+ if (node.operator !== "&&") {
3580
+ throw new Error(`Unexpected operator "${node.operator}"`);
3581
+ }
3582
+ return { ...node, type: "BinaryExpression", operator: "&" };
3583
+ }
3584
+ if (left_type === "Boolean") {
3585
+ if (isBooleanExpression(state, node.right)) {
3586
+ return node.right;
3587
+ }
1283
3588
  }
1284
3589
  break;
1285
3590
  }
1286
3591
  case "FunctionDeclaration":
1287
- if (node.body && evaluateFunction(node, null) !== false) {
3592
+ if (node.body && evaluateFunction(state, node, null) !== false) {
1288
3593
  node.optimizable = true;
1289
3594
  }
1290
3595
  break;
1291
3596
  }
1292
3597
  return null;
1293
3598
  }
1294
- function evaluateFunction(func, args) {
3599
+ function evaluateFunction(state, func, args) {
1295
3600
  if (!func.body || (args && args.length != func.params.length)) {
1296
3601
  return false;
1297
3602
  }
1298
3603
  const paramValues = args &&
1299
3604
  Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
1300
3605
  let ret = null;
1301
- const body = args
1302
- ? JSON.parse(JSON.stringify(func.body))
1303
- : func.body;
3606
+ const body = args ? cloneDeep(func.body) : func.body;
1304
3607
  try {
1305
3608
  traverseAst(body, (node) => {
1306
3609
  switch (node.type) {
@@ -1330,7 +3633,7 @@ function evaluateFunction(func, args) {
1330
3633
  }
1331
3634
  // fall through;
1332
3635
  default: {
1333
- const repl = optimizeNode(node);
3636
+ const repl = optimizeNode(state, node);
1334
3637
  if (repl && repl.type === "Literal")
1335
3638
  return repl;
1336
3639
  throw new Error("Didn't optimize");
@@ -1351,12 +3654,10 @@ function markFunctionCalled(state, func) {
1351
3654
  pushUnique(state.calledFunctions[func.id.name], func);
1352
3655
  }
1353
3656
  async function optimizeMonkeyC(fnMap, barrelList, config) {
1354
- const state = {
1355
- ...(await analyze(fnMap, barrelList, config)),
1356
- localsStack: [{}],
1357
- exposed: {},
1358
- calledFunctions: {},
1359
- };
3657
+ const state = (await analyze(fnMap, barrelList, config));
3658
+ state.localsStack = [{}];
3659
+ state.calledFunctions = {};
3660
+ state.usedByName = {};
1360
3661
  const replace = (node, old) => {
1361
3662
  if (node === false || node === null)
1362
3663
  return node;
@@ -1390,10 +3691,19 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1390
3691
  if (!objects) {
1391
3692
  return false;
1392
3693
  }
1393
- const obj = getLiteralFromDecls(objects);
3694
+ let obj = getLiteralFromDecls(objects);
1394
3695
  if (!obj) {
1395
3696
  return false;
1396
3697
  }
3698
+ while (obj.type === "BinaryExpression") {
3699
+ if (obj.left.type === "BinaryExpression" && obj.left.operator === "as") {
3700
+ obj = { ...obj, left: obj.left.left };
3701
+ }
3702
+ else {
3703
+ obj = { ...obj, left: { ...obj.left } };
3704
+ break;
3705
+ }
3706
+ }
1397
3707
  inPlaceReplacement(node, obj);
1398
3708
  return true;
1399
3709
  };
@@ -1436,6 +3746,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1436
3746
  f.type == "FunctionDeclaration" &&
1437
3747
  maybeCalled(f.node))) ||
1438
3748
  (sc.superClass && checkInherited(sc, name))));
3749
+ const renamer = (idnode) => {
3750
+ const ident = idnode.type === "Identifier" ? idnode : idnode.left;
3751
+ const locals = topLocals();
3752
+ const { map } = locals;
3753
+ if (map) {
3754
+ const declName = ident.name;
3755
+ const name = renameVariable(state, locals, declName);
3756
+ if (name) {
3757
+ const [, results] = state.lookupValue(ident);
3758
+ if (!results) {
3759
+ throw new Error(`Didn't find local ${declName} which needed renaming`);
3760
+ }
3761
+ if (results.length !== 1) {
3762
+ throw new Error(`Lookup of local ${declName} found more than one result`);
3763
+ }
3764
+ const parent = results[0].parent;
3765
+ if (!parent) {
3766
+ throw new Error(`No parent in lookup of local ${declName}`);
3767
+ }
3768
+ const decls = parent.decls;
3769
+ if (!decls || !hasProperty(decls, declName)) {
3770
+ throw new Error(`Missing decls in lookup of local ${declName}`);
3771
+ }
3772
+ if (hasProperty(decls, name)) {
3773
+ throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
3774
+ }
3775
+ if (decls[declName].length === 1) {
3776
+ decls[name] = decls[declName];
3777
+ delete decls[declName];
3778
+ }
3779
+ else {
3780
+ let i = decls[declName].length;
3781
+ while (i--) {
3782
+ const decl = decls[declName][i];
3783
+ if (decl === idnode ||
3784
+ (decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
3785
+ decls[declName].splice(i, 1);
3786
+ decls[name] = [decl];
3787
+ break;
3788
+ }
3789
+ }
3790
+ if (i < 0) {
3791
+ throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
3792
+ }
3793
+ }
3794
+ ident.name = name;
3795
+ }
3796
+ else {
3797
+ map[declName] = true;
3798
+ }
3799
+ }
3800
+ };
1439
3801
  state.pre = (node) => {
1440
3802
  switch (node.type) {
1441
3803
  case "ConditionalExpression":
@@ -1456,7 +3818,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1456
3818
  result = !!value.value;
1457
3819
  }
1458
3820
  if (result !== null) {
1459
- node.test = { type: "Literal", value: result };
3821
+ node.test = {
3822
+ type: "Literal",
3823
+ value: result,
3824
+ raw: result.toString(),
3825
+ };
1460
3826
  if (node.type === "IfStatement" ||
1461
3827
  node.type === "ConditionalExpression") {
1462
3828
  return [result ? "consequent" : "alternate"];
@@ -1475,7 +3841,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1475
3841
  return null;
1476
3842
  }
1477
3843
  case "EnumDeclaration":
1478
- return false;
3844
+ return [];
1479
3845
  case "ForStatement": {
1480
3846
  const map = topLocals().map;
1481
3847
  if (map) {
@@ -1484,43 +3850,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1484
3850
  break;
1485
3851
  }
1486
3852
  case "VariableDeclarator": {
1487
- const locals = topLocals();
1488
- const { map } = locals;
1489
- if (map) {
1490
- const declName = variableDeclarationName(node.id);
1491
- const name = renameVariable(state, locals, declName);
1492
- if (name) {
1493
- if (node.id.type === "Identifier") {
1494
- node.id.name = name;
1495
- }
1496
- else {
1497
- node.id.left.name = name;
1498
- }
1499
- }
1500
- else {
1501
- map[declName] = true;
1502
- }
1503
- }
3853
+ renamer(node.id);
1504
3854
  return ["init"];
1505
3855
  }
1506
3856
  case "CatchClause":
1507
3857
  if (node.param) {
1508
3858
  state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
1509
- const locals = topLocals();
1510
- const map = locals.map;
1511
- const declName = variableDeclarationName(node.param);
1512
- const name = renameVariable(state, locals, declName);
1513
- if (name) {
1514
- if (node.param.type === "Identifier") {
1515
- node.param.name = name;
1516
- }
1517
- else {
1518
- node.param.left.name = name;
1519
- }
1520
- }
1521
- else {
1522
- map[declName] = true;
1523
- }
3859
+ renamer(node.param);
1524
3860
  return ["body"];
1525
3861
  }
1526
3862
  break;
@@ -1536,14 +3872,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1536
3872
  break;
1537
3873
  case "UnaryExpression":
1538
3874
  if (node.operator == ":") {
1539
- // If we produce a Symbol, for a given name,
1540
- // its possible that someone uses that symbol
1541
- // indirectly, so we can't remove any enums or
1542
- // constants with that name (we can still replace
1543
- // uses of those constants though).
1544
- state.exposed[node.argument.name] = true;
1545
- // In any case, we can't replace *this* use of the
1546
- // symbol with its value...
3875
+ // node.argument is not a normal identifier.
3876
+ // don't visit it.
1547
3877
  return [];
1548
3878
  }
1549
3879
  break;
@@ -1555,29 +3885,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1555
3885
  if (typeof name === "string") {
1556
3886
  node.name = name;
1557
3887
  }
3888
+ const [, results] = state.lookupValue(node);
3889
+ if (results) {
3890
+ if (results.length !== 1 || results[0].results.length !== 1) {
3891
+ throw new Error(`Local ${node.name} had multiple lookup results`);
3892
+ }
3893
+ const parent = results[0].parent;
3894
+ if (!parent) {
3895
+ throw new Error(`Local ${node.name} had no parent`);
3896
+ }
3897
+ const decl = results[0].results[0];
3898
+ if (parent.type === "FunctionDeclaration" ||
3899
+ decl.type !== "VariableDeclarator") {
3900
+ // we can't optimize away function or catch parameters
3901
+ return [];
3902
+ }
3903
+ if (parent.type !== "BlockStatement") {
3904
+ throw new Error(`Local ${node.name} was not declared at block scope(??)`);
3905
+ }
3906
+ decl.used = true;
3907
+ }
1558
3908
  }
1559
3909
  }
1560
3910
  if (hasProperty(state.index, node.name)) {
1561
3911
  if (!lookupAndReplace(node)) {
1562
- state.exposed[node.name] = true;
3912
+ state.usedByName[node.name] = true;
1563
3913
  }
1564
3914
  }
1565
3915
  return [];
1566
3916
  }
1567
- case "MemberExpression":
1568
- if (node.property.type === "Identifier" && !node.computed) {
1569
- if (hasProperty(state.index, node.property.name)) {
3917
+ case "MemberExpression": {
3918
+ const property = isLookupCandidate(node);
3919
+ if (property) {
3920
+ if (hasProperty(state.index, property.name)) {
1570
3921
  if (lookupAndReplace(node)) {
1571
3922
  return false;
1572
3923
  }
1573
3924
  else {
1574
- state.exposed[node.property.name] = true;
3925
+ state.usedByName[property.name] = true;
1575
3926
  }
1576
3927
  }
1577
3928
  // Don't optimize the property.
1578
3929
  return ["object"];
1579
3930
  }
1580
3931
  break;
3932
+ }
3933
+ case "AssignmentExpression":
3934
+ case "UpdateExpression": {
3935
+ const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
3936
+ if (lhs.type === "Identifier") {
3937
+ const map = topLocals().map;
3938
+ if (map) {
3939
+ if (hasProperty(map, lhs.name)) {
3940
+ const name = map[lhs.name];
3941
+ if (typeof name === "string") {
3942
+ lhs.name = name;
3943
+ }
3944
+ }
3945
+ }
3946
+ }
3947
+ else if (lhs.type === "MemberExpression") {
3948
+ state.traverse(lhs.object);
3949
+ if (lhs.computed) {
3950
+ state.traverse(lhs.property);
3951
+ }
3952
+ }
3953
+ return node.type === "AssignmentExpression" ? ["right"] : [];
3954
+ }
1581
3955
  case "BlockStatement": {
1582
3956
  const map = topLocals().map;
1583
3957
  if (map) {
@@ -1593,7 +3967,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1593
3967
  node.params &&
1594
3968
  node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
1595
3969
  state.localsStack.push({ node, map });
1596
- const [parent] = state.stack.slice(-2);
3970
+ const [parent, self] = state.stack.slice(-2);
3971
+ if (state.currentFunction) {
3972
+ throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
3973
+ }
3974
+ state.currentFunction = self;
1597
3975
  if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
1598
3976
  let used = false;
1599
3977
  if (node.id.name == "initialize") {
@@ -1611,14 +3989,33 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1611
3989
  return null;
1612
3990
  };
1613
3991
  state.post = (node) => {
1614
- if (topLocals().node === node) {
3992
+ const locals = topLocals();
3993
+ if (locals.node === node) {
1615
3994
  state.localsStack.pop();
1616
3995
  }
1617
- const opt = optimizeNode(node);
3996
+ const opt = optimizeNode(state, node);
1618
3997
  if (opt) {
1619
3998
  return replace(opt, node);
1620
3999
  }
1621
4000
  switch (node.type) {
4001
+ case "FunctionDeclaration":
4002
+ if (!state.currentFunction) {
4003
+ throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
4004
+ }
4005
+ state.currentFunction.info = state.currentFunction.next_info;
4006
+ delete state.currentFunction.next_info;
4007
+ delete state.currentFunction;
4008
+ break;
4009
+ case "BlockStatement":
4010
+ if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
4011
+ node.body.splice(0, 1, ...node.body[0].body);
4012
+ }
4013
+ // fall through
4014
+ case "ForStatement":
4015
+ if (locals.map) {
4016
+ cleanupUnusedVars(state, node);
4017
+ }
4018
+ break;
1622
4019
  case "ConditionalExpression":
1623
4020
  case "IfStatement":
1624
4021
  if (node.test.type === "Literal" &&
@@ -1628,6 +4025,12 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1628
4025
  return false;
1629
4026
  return replace(rep, rep);
1630
4027
  }
4028
+ else if (node.type === "IfStatement" &&
4029
+ node.alternate &&
4030
+ node.alternate.type === "BlockStatement" &&
4031
+ !node.alternate.body.length) {
4032
+ delete node.alternate;
4033
+ }
1631
4034
  break;
1632
4035
  case "WhileStatement":
1633
4036
  if (node.test.type === "Literal" && node.test.value === false) {
@@ -1644,17 +4047,62 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1644
4047
  return replace(optimizeCall(state, node.argument, node), node.argument);
1645
4048
  }
1646
4049
  break;
4050
+ case "NewExpression":
4051
+ if (state.currentFunction) {
4052
+ const [, results] = state.lookup(node.callee);
4053
+ if (results) {
4054
+ recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
4055
+ }
4056
+ else {
4057
+ recordModifiedUnknown(state.currentFunction);
4058
+ }
4059
+ }
4060
+ break;
1647
4061
  case "CallExpression": {
1648
4062
  return replace(optimizeCall(state, node, null), node);
1649
4063
  }
1650
- case "AssignmentExpression":
1651
- if (node.operator === "=" &&
1652
- node.left.type === "Identifier" &&
1653
- node.right.type === "Identifier" &&
1654
- node.left.name === node.right.name) {
1655
- return { type: "Literal", value: null, raw: "null" };
4064
+ case "VariableDeclaration": {
4065
+ const locals = topLocals();
4066
+ if (locals.map &&
4067
+ locals.node &&
4068
+ locals.node.type === "BlockStatement") {
4069
+ let results;
4070
+ const declarations = node.declarations;
4071
+ let i = 0;
4072
+ let j = 0;
4073
+ while (i < node.declarations.length) {
4074
+ const decl = declarations[i++];
4075
+ if (decl.init && decl.init.type === "CallExpression") {
4076
+ const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
4077
+ if (!inlined)
4078
+ continue;
4079
+ if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
4080
+ throw new Error("Unexpected inlined result");
4081
+ }
4082
+ if (!results) {
4083
+ results = [];
4084
+ }
4085
+ delete decl.init;
4086
+ results.push(withLoc({
4087
+ ...node,
4088
+ declarations: declarations.slice(j, i),
4089
+ }, j ? declarations[j] : null, decl.id));
4090
+ results.push(inlined);
4091
+ j = i;
4092
+ }
4093
+ }
4094
+ if (results) {
4095
+ if (j < i) {
4096
+ results.push({
4097
+ ...node,
4098
+ declarations: declarations.slice(j, i),
4099
+ });
4100
+ }
4101
+ return results;
4102
+ }
1656
4103
  }
1657
4104
  break;
4105
+ }
1658
4106
  case "ExpressionStatement":
1659
4107
  if (node.expression.type === "CallExpression") {
1660
4108
  return replace(optimizeCall(state, node.expression, node), node.expression);
@@ -1686,6 +4134,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1686
4134
  }
1687
4135
  }
1688
4136
  break;
4137
+ case "AssignmentExpression":
4138
+ if (node.operator === "=" &&
4139
+ node.left.type === "Identifier" &&
4140
+ node.right.type === "Identifier" &&
4141
+ node.left.name === node.right.name) {
4142
+ return { type: "Literal", value: null, raw: "null" };
4143
+ }
4144
+ // fall through;
4145
+ case "UpdateExpression":
4146
+ if (state.currentFunction) {
4147
+ const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
4148
+ const [, results] = state.lookup(lhs);
4149
+ if (results) {
4150
+ recordModifiedDecls(state.currentFunction, results);
4151
+ }
4152
+ else {
4153
+ const id = lhs.type === "Identifier" ? lhs : isLookupCandidate(lhs);
4154
+ if (id) {
4155
+ recordModifiedName(state.currentFunction, id.name);
4156
+ }
4157
+ else {
4158
+ recordModifiedUnknown(state.currentFunction);
4159
+ }
4160
+ }
4161
+ }
4162
+ break;
1689
4163
  }
1690
4164
  return null;
1691
4165
  };
@@ -1693,12 +4167,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1693
4167
  collectNamespaces(f.ast, state);
1694
4168
  });
1695
4169
  state.calledFunctions = {};
1696
- state.exposed = {};
4170
+ state.exposed = state.nextExposed;
4171
+ state.nextExposed = {};
1697
4172
  Object.values(fnMap).forEach((f) => {
1698
4173
  collectNamespaces(f.ast, state);
1699
4174
  });
4175
+ state.exposed = state.nextExposed;
4176
+ state.nextExposed = {};
1700
4177
  delete state.pre;
1701
4178
  delete state.post;
4179
+ Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
1702
4180
  const cleanup = (node) => {
1703
4181
  switch (node.type) {
1704
4182
  case "ThisExpression":
@@ -1708,7 +4186,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1708
4186
  if (node.members.every((m) => {
1709
4187
  const name = "name" in m ? m.name : m.id.name;
1710
4188
  return (hasProperty(state.index, name) &&
1711
- !hasProperty(state.exposed, name));
4189
+ !hasProperty(state.exposed, name) &&
4190
+ !hasProperty(state.usedByName, name));
1712
4191
  })) {
1713
4192
  node.enumType = [
1714
4193
  ...new Set(node.members.map((m) => {
@@ -1751,7 +4230,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1751
4230
  case "VariableDeclaration": {
1752
4231
  node.declarations = node.declarations.filter((d) => {
1753
4232
  const name = variableDeclarationName(d.id);
1754
- return (!hasProperty(state.index, name) || hasProperty(state.exposed, name));
4233
+ return (!hasProperty(state.index, name) ||
4234
+ hasProperty(state.exposed, name) ||
4235
+ hasProperty(state.usedByName, name));
1755
4236
  });
1756
4237
  if (!node.declarations.length) {
1757
4238
  return false;
@@ -1784,7 +4265,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1784
4265
  }
1785
4266
  return null;
1786
4267
  };
1787
- Object.values(fnMap).forEach((f) => {
4268
+ Object.entries(fnMap).forEach(([name, f]) => {
1788
4269
  traverseAst(f.ast, undefined, (node) => {
1789
4270
  const ret = cleanup(node);
1790
4271
  if (ret === false) {
@@ -1792,16 +4273,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1792
4273
  }
1793
4274
  return ret;
1794
4275
  });
4276
+ if (state.config && state.config.checkBuildPragmas) {
4277
+ pragmaChecker(state, f.ast, state.diagnostics?.[name]);
4278
+ }
1795
4279
  });
1796
4280
  return state.diagnostics;
1797
4281
  }
1798
4282
  function optimizeCall(state, node, context) {
1799
4283
  const [name, results] = state.lookupNonlocal(node.callee);
1800
- const callees = results &&
1801
- results
1802
- .map((r) => r.results)
1803
- .flat()
1804
- .filter((c) => c.type === "FunctionDeclaration");
4284
+ const callees = results ? findCallees(results) : null;
1805
4285
  if (!callees || !callees.length) {
1806
4286
  const n = name ||
1807
4287
  ("name" in node.callee && node.callee.name) ||
@@ -1810,21 +4290,31 @@ function optimizeCall(state, node, context) {
1810
4290
  "name" in node.callee.property &&
1811
4291
  node.callee.property.name);
1812
4292
  if (n) {
1813
- state.exposed[n] = true;
4293
+ if (hasProperty(state.allFunctions, n)) {
4294
+ if (state.currentFunction) {
4295
+ recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
4296
+ }
4297
+ state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
4298
+ }
1814
4299
  }
1815
- else {
1816
- // There are unnamed CallExpressions, such as new [size]
1817
- // So there's nothing to do here.
4300
+ else if (state.currentFunction) {
4301
+ // I don't think this can happen: foo[x](args)
4302
+ // doesn't parse, so you can't even do things like
4303
+ // $.Toybox.Lang[:format]("fmt", [])
4304
+ recordModifiedUnknown(state.currentFunction);
1818
4305
  }
1819
4306
  return null;
1820
4307
  }
4308
+ if (state.currentFunction) {
4309
+ recordCalledFuncs(state.currentFunction, callees);
4310
+ }
1821
4311
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
1822
4312
  const callee = callees[0].node;
1823
4313
  if (!context &&
1824
4314
  callee.optimizable &&
1825
4315
  !callee.hasOverride &&
1826
4316
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
1827
- const ret = evaluateFunction(callee, node.arguments);
4317
+ const ret = evaluateFunction(state, callee, node.arguments);
1828
4318
  if (ret) {
1829
4319
  return ret;
1830
4320
  }
@@ -1984,11 +4474,8 @@ async function api_getApiMapping(state, barrelList) {
1984
4474
  return null;
1985
4475
  }
1986
4476
  }
1987
- function api_hasProperty(obj, prop) {
1988
- return obj ? Object.prototype.hasOwnProperty.call(obj, prop) : false;
1989
- }
1990
4477
  function api_isStateNode(node) {
1991
- return api_hasProperty(node, "node");
4478
+ return ast_hasProperty(node, "node");
1992
4479
  }
1993
4480
  function api_variableDeclarationName(node) {
1994
4481
  return ("left" in node ? node.left : node).name;
@@ -2008,7 +4495,7 @@ function checkOne(state, ns, decls, node, isStatic) {
2008
4495
  }
2009
4496
  return cls.superClass.reduce((result, sup) => {
2010
4497
  const sdecls = sup[decls];
2011
- const next = api_hasProperty(sdecls, node.name)
4498
+ const next = ast_hasProperty(sdecls, node.name)
2012
4499
  ? sdecls[node.name]
2013
4500
  : superChain(sup);
2014
4501
  return next ? (result ? result.concat(next) : next) : result;
@@ -2031,20 +4518,18 @@ function checkOne(state, ns, decls, node, isStatic) {
2031
4518
  return next ? (result ? result.concat(next) : next) : result;
2032
4519
  }, null);
2033
4520
  };
2034
- if (api_isStateNode(ns)) {
2035
- const ndecls = ns[decls];
2036
- if (api_hasProperty(ndecls, node.name)) {
2037
- return ndecls[node.name];
2038
- }
2039
- switch (ns.type) {
2040
- case "ClassDeclaration":
2041
- if (!isStatic) {
2042
- return superChain(ns) || superChainScopes(ns) || false;
2043
- }
2044
- // fall through
2045
- case "ModuleDeclaration":
2046
- return lookupInContext(ns) || false;
2047
- }
4521
+ const ndecls = ns[decls];
4522
+ if (ast_hasProperty(ndecls, node.name)) {
4523
+ return ndecls[node.name];
4524
+ }
4525
+ switch (ns.type) {
4526
+ case "ClassDeclaration":
4527
+ if (!isStatic) {
4528
+ return superChain(ns) || superChainScopes(ns) || false;
4529
+ }
4530
+ // fall through
4531
+ case "ModuleDeclaration":
4532
+ return lookupInContext(ns) || false;
2048
4533
  }
2049
4534
  return null;
2050
4535
  }
@@ -2067,6 +4552,13 @@ function sameLookupDefinition(a, b) {
2067
4552
  function api_sameLookupResult(a, b) {
2068
4553
  return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
2069
4554
  }
4555
+ function api_isLookupCandidate(node) {
4556
+ return node.computed
4557
+ ? node.property.type === "UnaryExpression" &&
4558
+ node.property.operator === ":" &&
4559
+ node.property.argument
4560
+ : node.property.type === "Identifier" && node.property;
4561
+ }
2070
4562
  /**
2071
4563
  *
2072
4564
  * @param state - The ProgramState
@@ -2087,9 +4579,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
2087
4579
  const stack = maybeStack || state.stack;
2088
4580
  switch (node.type) {
2089
4581
  case "MemberExpression": {
2090
- if (node.property.type != "Identifier" || node.computed)
4582
+ const property = api_isLookupCandidate(node);
4583
+ if (!property)
2091
4584
  break;
2092
- const property = node.property;
2093
4585
  let result;
2094
4586
  if (node.object.type === "ThisExpression") {
2095
4587
  [, result] = lookup(state, decls, node.property, name, stack, true);
@@ -2103,6 +4595,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
2103
4595
  result = results.reduce((current, lookupDef) => {
2104
4596
  const items = lookupDef.results
2105
4597
  .map((module) => {
4598
+ if (!api_isStateNode(module)) {
4599
+ return null;
4600
+ }
2106
4601
  const res = checkOne(state, module, decls, property, false);
2107
4602
  return res ? { parent: module, results: res } : null;
2108
4603
  })
@@ -2187,6 +4682,8 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
2187
4682
  }
2188
4683
  function api_collectNamespaces(ast, stateIn) {
2189
4684
  const state = (stateIn || {});
4685
+ if (!state.nextExposed)
4686
+ state.nextExposed = {};
2190
4687
  if (!state.index)
2191
4688
  state.index = {};
2192
4689
  if (!state.stack) {
@@ -2222,7 +4719,7 @@ function api_collectNamespaces(ast, stateIn) {
2222
4719
  state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
2223
4720
  ? { ...elm }
2224
4721
  : elm);
2225
- state.inType = false;
4722
+ state.inType = 0;
2226
4723
  state.traverse = (root) => ast_traverseAst(root, (node) => {
2227
4724
  try {
2228
4725
  if (state.shouldExclude && state.shouldExclude(node)) {
@@ -2230,6 +4727,13 @@ function api_collectNamespaces(ast, stateIn) {
2230
4727
  return [];
2231
4728
  }
2232
4729
  switch (node.type) {
4730
+ case "UnaryExpression":
4731
+ if (node.operator === ":" && !state.inType) {
4732
+ state.nextExposed[node.argument.name] = true;
4733
+ }
4734
+ break;
4735
+ case "AttributeList":
4736
+ return [];
2233
4737
  case "Program":
2234
4738
  if (state.stack.length != 1) {
2235
4739
  throw new Error("Unexpected stack length for Program node");
@@ -2238,7 +4742,7 @@ function api_collectNamespaces(ast, stateIn) {
2238
4742
  break;
2239
4743
  case "TypeSpecList":
2240
4744
  case "TypeSpecPart":
2241
- state.inType = true;
4745
+ state.inType++;
2242
4746
  break;
2243
4747
  case "ImportModule":
2244
4748
  case "Using": {
@@ -2284,6 +4788,16 @@ function api_collectNamespaces(ast, stateIn) {
2284
4788
  });
2285
4789
  }
2286
4790
  break;
4791
+ case "ForStatement":
4792
+ if (node.init && node.init.type === "VariableDeclaration") {
4793
+ state.stack.push({
4794
+ type: "BlockStatement",
4795
+ fullName: undefined,
4796
+ name: undefined,
4797
+ node: node,
4798
+ });
4799
+ }
4800
+ break;
2287
4801
  case "BlockStatement": {
2288
4802
  const [parent] = state.stack.slice(-1);
2289
4803
  if (parent.node === node ||
@@ -2313,7 +4827,7 @@ function api_collectNamespaces(ast, stateIn) {
2313
4827
  if (name) {
2314
4828
  if (!parent.decls)
2315
4829
  parent.decls = {};
2316
- if (api_hasProperty(parent.decls, name)) {
4830
+ if (ast_hasProperty(parent.decls, name)) {
2317
4831
  const what = node.type == "ModuleDeclaration" ? "type" : "node";
2318
4832
  const e = parent.decls[name].find((d) => api_isStateNode(d) && d[what] == elm[what]);
2319
4833
  if (e != null) {
@@ -2339,7 +4853,7 @@ function api_collectNamespaces(ast, stateIn) {
2339
4853
  elm.decls = { [name]: [elm] };
2340
4854
  if (!parent.type_decls)
2341
4855
  parent.type_decls = {};
2342
- if (!api_hasProperty(parent.type_decls, name)) {
4856
+ if (!ast_hasProperty(parent.type_decls, name)) {
2343
4857
  parent.type_decls[name] = [];
2344
4858
  }
2345
4859
  parent.type_decls[name].push(elm);
@@ -2350,20 +4864,22 @@ function api_collectNamespaces(ast, stateIn) {
2350
4864
  // an EnumDeclaration doesn't create a scope, but
2351
4865
  // it does create a type (if it has a name)
2352
4866
  case "EnumDeclaration": {
2353
- if (!node.id)
4867
+ if (!node.id) {
4868
+ state.inType++;
2354
4869
  break;
4870
+ }
2355
4871
  const [parent] = state.stack.slice(-1);
2356
4872
  const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
2357
4873
  node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
2358
4874
  }
2359
4875
  // fall through
2360
4876
  case "TypedefDeclaration": {
2361
- state.inType = true;
4877
+ state.inType++;
2362
4878
  const name = node.id.name;
2363
4879
  const [parent] = state.stack.slice(-1);
2364
4880
  if (!parent.type_decls)
2365
4881
  parent.type_decls = {};
2366
- if (!api_hasProperty(parent.type_decls, name)) {
4882
+ if (!ast_hasProperty(parent.type_decls, name)) {
2367
4883
  parent.type_decls[name] = [];
2368
4884
  }
2369
4885
  else if (parent.type_decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == node)) {
@@ -2387,7 +4903,7 @@ function api_collectNamespaces(ast, stateIn) {
2387
4903
  const stack = state.stackClone();
2388
4904
  node.declarations.forEach((decl) => {
2389
4905
  const name = api_variableDeclarationName(decl.id);
2390
- if (!api_hasProperty(decls, name)) {
4906
+ if (!ast_hasProperty(decls, name)) {
2391
4907
  decls[name] = [];
2392
4908
  }
2393
4909
  else if (decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == decl)) {
@@ -2402,7 +4918,7 @@ function api_collectNamespaces(ast, stateIn) {
2402
4918
  stack,
2403
4919
  });
2404
4920
  if (node.kind == "const") {
2405
- if (!api_hasProperty(state.index, name)) {
4921
+ if (!ast_hasProperty(state.index, name)) {
2406
4922
  state.index[name] = [];
2407
4923
  }
2408
4924
  (0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
@@ -2411,13 +4927,21 @@ function api_collectNamespaces(ast, stateIn) {
2411
4927
  break;
2412
4928
  }
2413
4929
  case "EnumStringBody": {
2414
- state.inType = false;
4930
+ if (state.inType !== 1) {
4931
+ throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${state.inType}.`);
4932
+ }
4933
+ state.inType--;
2415
4934
  const [parent] = state.stack.slice(-1);
2416
4935
  const values = parent.decls || (parent.decls = {});
2417
4936
  let prev = -1;
2418
4937
  node.members.forEach((m, i) => {
2419
4938
  if (m.type == "Identifier") {
2420
- prev += 1;
4939
+ if (typeof prev === "bigint") {
4940
+ prev += 1n;
4941
+ }
4942
+ else {
4943
+ prev += 1;
4944
+ }
2421
4945
  m = node.members[i] = {
2422
4946
  type: "EnumStringMember",
2423
4947
  loc: m.loc,
@@ -2427,7 +4951,7 @@ function api_collectNamespaces(ast, stateIn) {
2427
4951
  init: {
2428
4952
  type: "Literal",
2429
4953
  value: prev,
2430
- raw: prev.toString(),
4954
+ raw: prev.toString() + (typeof prev === "bigint" ? "l" : ""),
2431
4955
  enumType: m.enumType,
2432
4956
  loc: m.loc,
2433
4957
  start: m.start,
@@ -2448,11 +4972,11 @@ function api_collectNamespaces(ast, stateIn) {
2448
4972
  prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(init.raw)) {
2449
4973
  prev = init.value;
2450
4974
  }
2451
- if (!api_hasProperty(values, name)) {
4975
+ if (!ast_hasProperty(values, name)) {
2452
4976
  values[name] = [];
2453
4977
  }
2454
4978
  (0,external_util_cjs_namespaceObject.pushUnique)(values[name], m);
2455
- if (!api_hasProperty(state.index, name)) {
4979
+ if (!ast_hasProperty(state.index, name)) {
2456
4980
  state.index[name] = [];
2457
4981
  }
2458
4982
  (0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
@@ -2479,22 +5003,22 @@ function api_collectNamespaces(ast, stateIn) {
2479
5003
  if (state.post)
2480
5004
  ret = state.post(node, state);
2481
5005
  switch (type) {
2482
- // Don't clear inType for TypeSpecPart, since they
2483
- // generally occur in TypeSpecLists. But do clear it for
2484
- // SizedArrayExpression, since thats the only place they
2485
- // happen on their own.
2486
- case "SizedArrayExpression":
5006
+ case "TypeSpecPart":
2487
5007
  case "TypeSpecList":
2488
5008
  case "TypedefDeclaration":
2489
5009
  case "EnumDeclaration":
2490
- state.inType = false;
5010
+ state.inType--;
2491
5011
  break;
2492
5012
  case "EnumStringBody":
2493
- state.inType = true;
5013
+ state.inType++;
2494
5014
  break;
2495
5015
  }
2496
5016
  const [parent] = state.stack.slice(-1);
2497
5017
  if (parent.node === node ||
5018
+ // The pre function might cause node.body to be skipped,
5019
+ // so we need to check here, just in case.
5020
+ // (this actually happens with prettier-extenison-monkeyc's
5021
+ // findItemsByRange)
2498
5022
  (node.type === "CatchClause" && parent.node === node.body)) {
2499
5023
  delete parent.usings;
2500
5024
  delete parent.imports;
@@ -2513,6 +5037,9 @@ function api_collectNamespaces(ast, stateIn) {
2513
5037
  }
2514
5038
  });
2515
5039
  state.traverse(ast);
5040
+ if (state.inType) {
5041
+ throw new Error(`inType was non-zero on exit: ${state.inType}`);
5042
+ }
2516
5043
  if (state.stack.length != 1) {
2517
5044
  throw new Error("Invalid AST!");
2518
5045
  }
@@ -2547,7 +5074,7 @@ function api_formatAst(node, monkeyCSource = null) {
2547
5074
  // json. The parser knows to just treat the last line of the input
2548
5075
  // as the ast itself, and the printers will find what they're
2549
5076
  // looking for in the source.
2550
- const source = (monkeyCSource || "") + "\n" + JSON.stringify(node);
5077
+ const source = (monkeyCSource || "") + "\n" + (0,prettier_plugin_monkeyc_namespaceObject.serializeMonkeyC)(node);
2551
5078
  return external_prettier_namespaceObject.format(source, {
2552
5079
  parser: "monkeyc-json",
2553
5080
  plugins: [(prettier_plugin_monkeyc_default())],
@@ -2592,7 +5119,7 @@ function findUsing(state, stack, using) {
2592
5119
  find(node.object);
2593
5120
  name = node.property.name;
2594
5121
  }
2595
- if (api_hasProperty(module.decls, name)) {
5122
+ if (ast_hasProperty(module.decls, name)) {
2596
5123
  const decls = module.decls[name];
2597
5124
  if (decls &&
2598
5125
  decls.length === 1 &&
@@ -2615,7 +5142,7 @@ function findUsing(state, stack, using) {
2615
5142
  function findUsingForNode(state, stack, i, node, isType) {
2616
5143
  while (i >= 0) {
2617
5144
  const si = stack[i--];
2618
- if (api_hasProperty(si.usings, node.name)) {
5145
+ if (ast_hasProperty(si.usings, node.name)) {
2619
5146
  const using = si.usings[node.name];
2620
5147
  const module = findUsing(state, stack, using);
2621
5148
  return module && [module];
@@ -2625,7 +5152,7 @@ function findUsingForNode(state, stack, i, node, isType) {
2625
5152
  const using = si.imports[j];
2626
5153
  const module = findUsing(state, stack, using);
2627
5154
  if (module) {
2628
- if (api_hasProperty(module.type_decls, node.name)) {
5155
+ if (ast_hasProperty(module.type_decls, node.name)) {
2629
5156
  return module.type_decls[node.name];
2630
5157
  }
2631
5158
  }
@@ -2634,6 +5161,38 @@ function findUsingForNode(state, stack, i, node, isType) {
2634
5161
  }
2635
5162
  return null;
2636
5163
  }
5164
+ const invokeInfo = {};
5165
+ const toyboxFnInfo = {};
5166
+ function api_getApiFunctionInfo(func) {
5167
+ if (func.fullName === "$.Toybox.Lang.Method.invoke" ||
5168
+ (func.node.params &&
5169
+ func.node.params.some((param) => param.type === "BinaryExpression" &&
5170
+ param.right.ts.some((tsp) => tsp.type === "TypeSpecPart" && tsp.callspec)))) {
5171
+ if (!invokeInfo.calledFuncs) {
5172
+ invokeInfo.modifiedDecls = new Set();
5173
+ invokeInfo.calledFuncs = new Set();
5174
+ invokeInfo.callsExposed = true;
5175
+ }
5176
+ if (func.name === "initialize") {
5177
+ const top = func.stack[func.stack.length - 1];
5178
+ if (top.type === "ClassDeclaration") {
5179
+ top.hasInvoke = true;
5180
+ }
5181
+ }
5182
+ return invokeInfo;
5183
+ }
5184
+ if (!toyboxFnInfo.calledFuncs) {
5185
+ toyboxFnInfo.modifiedDecls = new Set();
5186
+ toyboxFnInfo.calledFuncs = new Set();
5187
+ toyboxFnInfo.resolvedDecls = new Set();
5188
+ }
5189
+ return toyboxFnInfo;
5190
+ }
5191
+ function api_markInvokeClassMethod(func) {
5192
+ func.info = invokeInfo;
5193
+ }
5194
+
5195
+ })();
2637
5196
 
2638
5197
  var __webpack_export_target__ = exports;
2639
5198
  for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];