@markw65/monkeyc-optimizer 1.0.30 → 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,4 +1,4 @@
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
3
  /******/ var __webpack_modules__ = ({
4
4
 
@@ -261,9 +261,12 @@ __webpack_require__.d(__webpack_exports__, {
261
261
  "collectNamespaces": () => (/* binding */ api_collectNamespaces),
262
262
  "findUsingForNode": () => (/* binding */ findUsingForNode),
263
263
  "formatAst": () => (/* binding */ api_formatAst),
264
+ "getApiFunctionInfo": () => (/* binding */ api_getApiFunctionInfo),
264
265
  "getApiMapping": () => (/* binding */ api_getApiMapping),
265
266
  "hasProperty": () => (/* reexport */ ast_hasProperty),
267
+ "isLookupCandidate": () => (/* binding */ api_isLookupCandidate),
266
268
  "isStateNode": () => (/* binding */ api_isStateNode),
269
+ "markInvokeClassMethod": () => (/* binding */ api_markInvokeClassMethod),
267
270
  "sameLookupResult": () => (/* binding */ api_sameLookupResult),
268
271
  "traverseAst": () => (/* reexport */ ast_traverseAst),
269
272
  "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
@@ -464,21 +467,150 @@ function ast_withLoc(node, start, end) {
464
467
  }
465
468
  return node;
466
469
  }
467
- function ast_withLocDeep(node, start, end) {
468
- node = ast_withLoc({ ...node }, start, end);
470
+ function ast_withLocDeep(node, start, end, inplace) {
471
+ node = ast_withLoc(inplace ? node : { ...node }, start, end);
469
472
  for (const key of mctreeTypeInfo[node.type].keys) {
470
473
  const value = node[key];
471
474
  if (!value)
472
475
  continue;
473
- const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end) : v;
476
+ const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end, inplace) : v;
474
477
  const repl = Array.isArray(value) ? value.map(fix) : fix(value);
475
- node[key] = repl;
478
+ inplace || (node[key] = repl);
476
479
  }
477
480
  return node;
478
481
  }
482
+ function ast_cloneDeep(node) {
483
+ return ast_withLocDeep(node, null);
484
+ }
479
485
 
480
486
  ;// CONCATENATED MODULE: external "./api.cjs"
481
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
+
482
614
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
483
615
 
484
616
 
@@ -539,6 +671,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
539
671
 
540
672
 
541
673
 
674
+
542
675
  function getArgSafety(state, func, args, requireAll) {
543
676
  // determine whether decl might be changed by a function call
544
677
  // or assignment during the evaluation of FunctionStateNode.
@@ -571,8 +704,9 @@ function getArgSafety(state, func, args, requireAll) {
571
704
  }
572
705
  };
573
706
  const safeArgs = [];
707
+ const argDecls = [];
574
708
  let allSafe = true;
575
- if (!args.every((arg) => {
709
+ if (!args.every((arg, i) => {
576
710
  switch (arg.type) {
577
711
  case "Literal":
578
712
  safeArgs.push(true);
@@ -586,13 +720,17 @@ function getArgSafety(state, func, args, requireAll) {
586
720
  safeArgs.push(null);
587
721
  return !requireAll;
588
722
  }
589
- const safety = getSafety(results[0].results[0]);
723
+ const decl = results[0].results[0];
724
+ const safety = getSafety(decl);
590
725
  safeArgs.push(safety);
591
726
  if (!safety) {
592
727
  allSafe = false;
593
728
  if (safety === null) {
594
729
  return !requireAll;
595
730
  }
731
+ else if (decl.type === "VariableDeclarator") {
732
+ argDecls[i] = decl;
733
+ }
596
734
  }
597
735
  return true;
598
736
  }
@@ -605,34 +743,91 @@ function getArgSafety(state, func, args, requireAll) {
605
743
  }
606
744
  if (allSafe && requireAll)
607
745
  return true;
608
- let callSeen = false;
746
+ const callsSeen = new Set();
747
+ const modifiedDecls = new Set();
748
+ let modifiedUnknown = false;
609
749
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
610
750
  // look for uses of "unsafe" args that occur after a call.
611
751
  // use post to do the checking, because arguments are evaluated
612
752
  // prior to the call, so eg "return f(x.y);" is fine, but
613
753
  // "return f()+x.y" is not.
614
- traverseAst(func.node.body, null, (node) => {
615
- switch (node.type) {
616
- case "AssignmentExpression":
617
- case "UpdateExpression": {
618
- const v = node.type == "UpdateExpression" ? node.argument : node.left;
619
- if (v.type === "Identifier" && hasProperty(params, v.name)) {
620
- 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;
621
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
+ }
622
820
  }
623
- // fall through
624
- case "CallExpression":
625
- case "NewExpression":
626
- callSeen = true;
627
- break;
628
- case "Identifier":
629
- if (callSeen &&
630
- hasProperty(params, node.name) &&
631
- !safeArgs[params[node.name]]) {
632
- safeArgs[params[node.name]] = null;
633
- }
634
- }
635
- });
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
+ }
636
831
  return safeArgs;
637
832
  }
638
833
  function canInline(state, func, args) {
@@ -737,9 +932,6 @@ function processInlineBody(state, func, call, root, params) {
737
932
  state.pre = (node) => {
738
933
  if (failed)
739
934
  return [];
740
- node.start = call.start;
741
- node.end = call.end;
742
- node.loc = call.loc;
743
935
  if (replacements.has(node))
744
936
  return false;
745
937
  const result = pre(node, state);
@@ -754,6 +946,7 @@ function processInlineBody(state, func, call, root, params) {
754
946
  if (params[paramName] >= 0)
755
947
  return null;
756
948
  const name = renameVariable(state, locals, paramName) || paramName;
949
+ locals.map[name] = true;
757
950
  return {
758
951
  type: "VariableDeclarator",
759
952
  id: { type: "Identifier", name },
@@ -772,31 +965,49 @@ function processInlineBody(state, func, call, root, params) {
772
965
  }
773
966
  return result;
774
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
+ };
775
987
  state.post = (node) => {
776
988
  if (failed)
777
989
  return post(node, state);
778
990
  let replacement = null;
779
991
  switch (node.type) {
780
- case "Identifier": {
781
- if (state.inType)
782
- break;
783
- if (hasProperty(params, node.name)) {
784
- const ix = params[node.name];
785
- if (ix >= 0) {
786
- replacement = { ...call.arguments[ix] };
787
- replacements.add(replacement);
788
- return replacement;
992
+ case "AssignmentExpression":
993
+ if (node.left.type === "Identifier") {
994
+ const rep = fixId(node.left);
995
+ if (rep) {
996
+ node.left = rep;
789
997
  }
790
- break;
791
998
  }
792
- replacement = fixNodeScope(state, node, func.stack);
793
- if (!replacement) {
794
- failed = true;
795
- inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
796
- 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
+ }
797
1006
  }
798
1007
  break;
799
- }
1008
+ case "Identifier":
1009
+ replacement = fixId(node);
1010
+ break;
800
1011
  }
801
1012
  const ret = post(replacement || node, state);
802
1013
  return ret === false || ret ? ret : replacement;
@@ -965,7 +1176,7 @@ function inlineWithArgs(state, func, call, context) {
965
1176
  }
966
1177
  }
967
1178
  }
968
- const body = JSON.parse(JSON.stringify(func.node.body));
1179
+ const body = cloneDeep(func.node.body);
969
1180
  const safeArgs = getArgSafety(state, func, call.arguments, false);
970
1181
  const params = Object.fromEntries(func.node.params.map((param, i) => {
971
1182
  const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
@@ -1014,18 +1225,19 @@ function inlineWithArgs(state, func, call, context) {
1014
1225
  --block.body.length;
1015
1226
  }
1016
1227
  }
1228
+ withLocDeep(body, context, context, true);
1017
1229
  return body;
1018
1230
  }
1019
1231
  function inliner_inlineFunction(state, func, call, context) {
1020
1232
  if (context) {
1021
1233
  return inlineWithArgs(state, func, call, context);
1022
1234
  }
1023
- const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
1235
+ const retArg = cloneDeep(func.node.body.body[0].argument);
1024
1236
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
1025
1237
  const map = fixupLocalsMap(state);
1026
1238
  const ret = processInlineBody(state, func, call, retArg, params);
1027
1239
  state.localsStack[state.localsStack.length - 1].map = map;
1028
- return ret;
1240
+ return ret && withLocDeep(ret, call, call, true);
1029
1241
  }
1030
1242
  function applyTypeIfNeeded(node) {
1031
1243
  if ("enumType" in node && node.enumType) {
@@ -1130,6 +1342,121 @@ function fixNodeScope(state, lookupNode, nodeStack) {
1130
1342
  return null;
1131
1343
  }
1132
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
+
1133
1460
  ;// CONCATENATED MODULE: external "./util.cjs"
1134
1461
  const external_util_cjs_namespaceObject = require("./util.cjs");
1135
1462
  ;// CONCATENATED MODULE: ./src/control-flow.ts
@@ -1652,6 +1979,7 @@ var priorityqueuejs = __webpack_require__(2789);
1652
1979
 
1653
1980
 
1654
1981
 
1982
+
1655
1983
  /**
1656
1984
  * This implements a pseudo Partial Redundancy Elimination
1657
1985
  * pass. It isn't quite like traditional PRE because we're
@@ -1721,7 +2049,7 @@ function pre_sizeBasedPRE(state, func) {
1721
2049
  return;
1722
2050
  }
1723
2051
  const { graph: head, identifiers } = buildPREGraph(state, func);
1724
- const candidates = computeAttributes(head);
2052
+ const candidates = computeAttributes(state, head);
1725
2053
  if (candidates) {
1726
2054
  if (logging) {
1727
2055
  console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
@@ -1741,8 +2069,10 @@ function pre_sizeBasedPRE(state, func) {
1741
2069
  let i = 0;
1742
2070
  do {
1743
2071
  name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
1744
- if (!identifiers.has(name))
2072
+ if (!identifiers.has(name)) {
2073
+ identifiers.add(name);
1745
2074
  break;
2075
+ }
1746
2076
  i++;
1747
2077
  } while (true);
1748
2078
  declMap.set(decl, name);
@@ -1833,7 +2163,7 @@ function buildPREGraph(state, func) {
1833
2163
  case "ParenthesizedExpression":
1834
2164
  break;
1835
2165
  case "Literal":
1836
- if (!node.value && refCost(node) > LocalRefCost) {
2166
+ if (refCost(node) > LocalRefCost) {
1837
2167
  let decl = literals.get(node.value);
1838
2168
  if (!decl) {
1839
2169
  decl = node;
@@ -1917,10 +2247,18 @@ function buildPREGraph(state, func) {
1917
2247
  }
1918
2248
  break;
1919
2249
  }
1920
- case "NewExpression":
1921
- case "CallExpression":
2250
+ case "NewExpression": {
2251
+ const [, results] = state.lookup(node.callee);
2252
+ const callees = results ? findCalleesForNew(results) : null;
1922
2253
  liveDef(null, stmt);
1923
- return { type: "mod", node, mayThrow };
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
+ }
1924
2262
  default:
1925
2263
  if (!isExpression(node))
1926
2264
  break;
@@ -1936,12 +2274,6 @@ function buildPREGraph(state, func) {
1936
2274
  function anticipatedDecls() {
1937
2275
  return new Map();
1938
2276
  }
1939
- function cloneSet(ae) {
1940
- return new Set(ae);
1941
- }
1942
- function mergeSet(a, b) {
1943
- b.forEach((event) => a.add(event));
1944
- }
1945
2277
  function equalSet(a, b) {
1946
2278
  if (a.size != b.size)
1947
2279
  return false;
@@ -2020,6 +2352,7 @@ function refCost(node) {
2020
2352
  switch (typeof node.value) {
2021
2353
  case "string":
2022
2354
  return 5;
2355
+ case "bigint":
2023
2356
  case "number":
2024
2357
  return 5;
2025
2358
  case "boolean":
@@ -2081,7 +2414,7 @@ function candidateCost(candState) {
2081
2414
  cost += defCost(candState.node) * boundarySize;
2082
2415
  return cost;
2083
2416
  }
2084
- function computeAttributes(head) {
2417
+ function computeAttributes(state, head) {
2085
2418
  const order = getPostOrder(head);
2086
2419
  order.forEach((block, i) => {
2087
2420
  block.order = i;
@@ -2190,7 +2523,9 @@ function computeAttributes(head) {
2190
2523
  curState.forEach((candidates, decl) => {
2191
2524
  if (decl.type === "VariableDeclarator" &&
2192
2525
  decl.node.kind === "var" &&
2193
- candidates.live) {
2526
+ candidates.live &&
2527
+ (!event.callees ||
2528
+ event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
2194
2529
  candidates.ant.add(getMod(event, decl, candidates.node));
2195
2530
  candidates.live = false;
2196
2531
  }
@@ -2379,20 +2714,26 @@ function applyReplacements(func, nodeMap, declMap) {
2379
2714
  stmtStack.pop();
2380
2715
  const events = nodeMap.get(node);
2381
2716
  if (events) {
2382
- if (events.length === 1) {
2383
- if (events[0].type === "ref") {
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
+ }
2384
2722
  if (node.type !== "Identifier" &&
2385
2723
  node.type !== "MemberExpression" &&
2386
2724
  node.type !== "Literal") {
2387
2725
  throw new Error(`Ref found, but wrong type of node: ${node.type}`);
2388
2726
  }
2389
- const name = declMap.get(events[0].decl);
2727
+ const name = declMap.get(event.decl);
2390
2728
  if (!name) {
2391
2729
  throw new Error(`No replacement found for "${formatAst(node)}"`);
2392
2730
  }
2393
2731
  return ident(name, node);
2394
2732
  }
2395
- else if (events[0].type === "def") {
2733
+ if (event.type === "def") {
2734
+ if (ret) {
2735
+ throw new Error(`def found when there was already a replacement for this node`);
2736
+ }
2396
2737
  if (node.type !== "AssignmentExpression" &&
2397
2738
  node.type !== "UpdateExpression") {
2398
2739
  throw new Error(`Def found, but wrong type of node: ${node.type}`);
@@ -2400,7 +2741,7 @@ function applyReplacements(func, nodeMap, declMap) {
2400
2741
  const target = node.type === "AssignmentExpression"
2401
2742
  ? node.left
2402
2743
  : node.argument;
2403
- const name = declMap.get(events[0].decl);
2744
+ const name = declMap.get(event.decl);
2404
2745
  if (!name) {
2405
2746
  throw new Error(`No replacement found for "${formatAst(target)}"`);
2406
2747
  }
@@ -2419,20 +2760,24 @@ function applyReplacements(func, nodeMap, declMap) {
2419
2760
  }
2420
2761
  return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
2421
2762
  }
2422
- }
2423
- events.forEach((event) => {
2424
- if (event.type !== "mod") {
2425
- throw new Error(`Unexpected ${event.type} found amongst multiple events`);
2426
- }
2427
- if (!event.decl) {
2428
- throw new Error(`Unexpected null decl on mod event`);
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);
2429
2772
  }
2430
- let pending = pendingMap.get(stmt);
2431
- if (!pending) {
2432
- pendingMap.set(stmt, (pending = new Set()));
2773
+ else {
2774
+ throw new Error(`Unexpected ${event.type} found`);
2433
2775
  }
2434
- pending.add(event);
2435
- });
2776
+ return ret;
2777
+ }, null);
2778
+ if (ret) {
2779
+ return ret;
2780
+ }
2436
2781
  }
2437
2782
  const pending = pendingMap.get(node);
2438
2783
  if (node.type === "SequenceExpression") {
@@ -2506,6 +2851,148 @@ function applyReplacements(func, nodeMap, declMap) {
2506
2851
  });
2507
2852
  }
2508
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
+ }
2994
+ }
2995
+
2509
2996
  ;// CONCATENATED MODULE: ./src/visitor.ts
2510
2997
 
2511
2998
  function visitor_visitReferences(state, ast, name, defn, callback) {
@@ -2570,14 +3057,16 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
2570
3057
  return checkResults(state.lookup(node), node);
2571
3058
  }
2572
3059
  break;
2573
- case "MemberExpression":
2574
- if (!node.computed && node.property.type === "Identifier") {
2575
- 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) {
2576
3064
  return checkResults(state.lookup(node), node) || ["object"];
2577
3065
  }
2578
3066
  return ["object"];
2579
3067
  }
2580
3068
  break;
3069
+ }
2581
3070
  case "MethodDefinition": {
2582
3071
  if (!state.inType) {
2583
3072
  throw new Error("Method definition outside of type!");
@@ -2586,7 +3075,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
2586
3075
  node.params.forEach((param) => {
2587
3076
  if (param.type == "BinaryExpression") {
2588
3077
  state.traverse(param.right);
2589
- state.inType = true;
2590
3078
  }
2591
3079
  });
2592
3080
  }
@@ -2609,6 +3097,9 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
2609
3097
 
2610
3098
 
2611
3099
 
3100
+
3101
+
3102
+
2612
3103
  function collectClassInfo(state) {
2613
3104
  const toybox = state.stack[0].decls["Toybox"][0];
2614
3105
  const lang = toybox.decls["Lang"][0];
@@ -2668,8 +3159,7 @@ function collectClassInfo(state) {
2668
3159
  c.decls &&
2669
3160
  Object.values(c.decls).forEach((funcs) => {
2670
3161
  funcs.forEach((f) => {
2671
- if (isStateNode(f) &&
2672
- f.type === "FunctionDeclaration" &&
3162
+ if (f.type === "FunctionDeclaration" &&
2673
3163
  hasProperty(cls.decls, f.name)) {
2674
3164
  f.node.hasOverride = true;
2675
3165
  }
@@ -2682,6 +3172,15 @@ function collectClassInfo(state) {
2682
3172
  state.allClasses.forEach((elm) => {
2683
3173
  if (elm.superClass)
2684
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
+ }
2685
3184
  });
2686
3185
  }
2687
3186
  function getFileSources(fnMap) {
@@ -2721,7 +3220,7 @@ async function analyze(fnMap, barrelList, config) {
2721
3220
  const preState = {
2722
3221
  fnMap,
2723
3222
  config,
2724
- allFunctions: [],
3223
+ allFunctions: {},
2725
3224
  allClasses: [],
2726
3225
  shouldExclude(node) {
2727
3226
  if ("attrs" in node &&
@@ -2751,11 +3250,6 @@ async function analyze(fnMap, barrelList, config) {
2751
3250
  pre(node, state) {
2752
3251
  switch (node.type) {
2753
3252
  case "FunctionDeclaration":
2754
- if (markApi) {
2755
- node.body = null;
2756
- break;
2757
- }
2758
- // falls through
2759
3253
  case "ModuleDeclaration":
2760
3254
  case "ClassDeclaration": {
2761
3255
  const [scope] = state.stack.slice(-1);
@@ -2766,7 +3260,18 @@ async function analyze(fnMap, barrelList, config) {
2766
3260
  (scope.node.attrs &&
2767
3261
  scope.node.attrs.access &&
2768
3262
  scope.node.attrs.access.includes("static"));
2769
- 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
+ }
2770
3275
  }
2771
3276
  else if (scope.type === "ClassDeclaration") {
2772
3277
  state.allClasses.push(scope);
@@ -2791,7 +3296,7 @@ async function analyze(fnMap, barrelList, config) {
2791
3296
  value.hasTests = hasTests;
2792
3297
  });
2793
3298
  delete state.shouldExclude;
2794
- delete state.post;
3299
+ delete state.pre;
2795
3300
  collectClassInfo(state);
2796
3301
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
2797
3302
  ? config?.checkInvalidSymbols || "WARNING"
@@ -2814,6 +3319,8 @@ async function analyze(fnMap, barrelList, config) {
2814
3319
  });
2815
3320
  });
2816
3321
  }
3322
+ state.exposed = state.nextExposed;
3323
+ state.nextExposed = {};
2817
3324
  return state;
2818
3325
  }
2819
3326
  function compareLiteralLike(a, b) {
@@ -2859,15 +3366,12 @@ function getLiteralNode(node) {
2859
3366
  if (node.argument.type != "Literal")
2860
3367
  return null;
2861
3368
  switch (node.operator) {
2862
- case "-":
2863
- if (typeof node.argument.value == "number") {
2864
- return {
2865
- ...node.argument,
2866
- value: -node.argument.value,
2867
- raw: "-" + node.argument.value,
2868
- enumType: node.enumType,
2869
- };
3369
+ case "-": {
3370
+ const [arg, type] = getNodeValue(node.argument);
3371
+ if (type === "Number" || type === "Long") {
3372
+ return replacementLiteral(arg, -arg.value, type);
2870
3373
  }
3374
+ }
2871
3375
  }
2872
3376
  }
2873
3377
  return null;
@@ -2886,29 +3390,29 @@ function getNodeValue(node) {
2886
3390
  if (node.type != "Literal") {
2887
3391
  return [null, null];
2888
3392
  }
2889
- 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;
2890
3397
  if (type === "number") {
2891
- const match = node.raw && LiteralIntegerRe.exec(node.raw);
3398
+ const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
2892
3399
  if (match) {
2893
- type = match[2] == "l" ? "Long" : "Number";
2894
- }
2895
- else if (node.raw && node.raw.endsWith("d")) {
2896
- type = "Double";
2897
- }
2898
- else {
2899
- type = "Float";
3400
+ return match[2] === "l" || match[2] === "L"
3401
+ ? [node, "Long"]
3402
+ : [node, "Number"];
2900
3403
  }
3404
+ return [node, node.raw.endsWith("d") ? "Double" : "Float"];
2901
3405
  }
2902
- else if (type === "string") {
2903
- type = "String";
3406
+ if (type === "bigint") {
3407
+ return [node, "Long"];
2904
3408
  }
2905
- else if (type === "boolean") {
2906
- type = "Boolean";
3409
+ if (type === "string") {
3410
+ return [node, "String"];
2907
3411
  }
2908
- else {
2909
- type = "Unknown";
3412
+ if (type === "boolean") {
3413
+ return [node, "Boolean"];
2910
3414
  }
2911
- return [node, type];
3415
+ throw new Error(`Literal has unknown type '${type}'`);
2912
3416
  }
2913
3417
  function fullTypeName(state, tsp) {
2914
3418
  if (typeof tsp.name === "string") {
@@ -2953,6 +3457,46 @@ function isBooleanExpression(state, node) {
2953
3457
  }
2954
3458
  return false;
2955
3459
  }
3460
+ function replacementLiteral(arg, value, type) {
3461
+ if (typeof value === "boolean") {
3462
+ type = "Boolean";
3463
+ }
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));
3469
+ }
3470
+ return {
3471
+ ...arg,
3472
+ value,
3473
+ raw: value.toString() + (type === "Long" ? "l" : ""),
3474
+ };
3475
+ }
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
+ };
2956
3500
  function optimizeNode(state, node) {
2957
3501
  switch (node.type) {
2958
3502
  case "UnaryExpression": {
@@ -2967,29 +3511,17 @@ function optimizeNode(state, node) {
2967
3511
  break;
2968
3512
  case "-":
2969
3513
  if (type === "Number" || type === "Long") {
2970
- return {
2971
- ...arg,
2972
- value: -arg.value,
2973
- raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
2974
- };
3514
+ return replacementLiteral(arg, -arg.value, type);
2975
3515
  }
2976
3516
  break;
2977
3517
  case "!":
2978
3518
  case "~":
2979
3519
  {
2980
- let value;
2981
3520
  if (type === "Number" || type === "Long") {
2982
- value = -arg.value - 1;
3521
+ return replacementLiteral(arg, ~BigInt(arg.value), type);
2983
3522
  }
2984
- else if (type === "Boolean" && node.operator == "!") {
2985
- value = !arg.value;
2986
- }
2987
- if (value !== undefined) {
2988
- return {
2989
- ...arg,
2990
- value,
2991
- raw: value.toString() + (type === "Long" ? "l" : ""),
2992
- };
3523
+ if (type === "Boolean" && node.operator == "!") {
3524
+ return replacementLiteral(arg, !arg.value, type);
2993
3525
  }
2994
3526
  }
2995
3527
  break;
@@ -2997,27 +3529,6 @@ function optimizeNode(state, node) {
2997
3529
  break;
2998
3530
  }
2999
3531
  case "BinaryExpression": {
3000
- const operators = {
3001
- "+": (left, right) => left + right,
3002
- "-": (left, right) => left - right,
3003
- "*": (left, right) => left * right,
3004
- "/": (left, right) => Math.trunc(left / right),
3005
- "%": (left, right) => left % right,
3006
- "&": (left, right, type) => type === "Number" ? left & right : null,
3007
- "|": (left, right, type) => type === "Number" ? left | right : null,
3008
- "^": (left, right, type) => type === "Number" ? left ^ right : null,
3009
- "<<": (left, right, type) => type === "Number" ? left << right : null,
3010
- ">>": (left, right, type) => type === "Number" ? left >> right : null,
3011
- "==": (left, right) => left == right,
3012
- "!=": (left, right) => left != right,
3013
- "<=": (left, right) => left <= right,
3014
- ">=": (left, right) => left >= right,
3015
- "<": (left, right) => left < right,
3016
- ">": (left, right) => left > right,
3017
- as: null,
3018
- instanceof: null,
3019
- has: null,
3020
- };
3021
3532
  const op = operators[node.operator];
3022
3533
  if (op) {
3023
3534
  const [left, left_type] = getNodeValue(node.left);
@@ -3025,23 +3536,22 @@ function optimizeNode(state, node) {
3025
3536
  if (!left || !right)
3026
3537
  break;
3027
3538
  let value = null;
3028
- if (left_type != right_type ||
3029
- (left_type != "Number" && left_type != "Long")) {
3539
+ let type;
3540
+ if ((left_type != "Number" && left_type != "Long") ||
3541
+ left_type != right_type) {
3030
3542
  if (node.operator !== "==" && node.operator !== "!=") {
3031
3543
  break;
3032
3544
  }
3033
3545
  value = operators[node.operator](left.value, right.value);
3546
+ type = "Boolean";
3034
3547
  }
3035
3548
  else {
3036
- value = op(left.value, right.value, left_type);
3549
+ type = left_type;
3550
+ value = op(BigInt(left.value), BigInt(right.value));
3037
3551
  }
3038
3552
  if (value === null)
3039
3553
  break;
3040
- return {
3041
- ...left,
3042
- value,
3043
- raw: value.toString() + (left_type === "Long" ? "l" : ""),
3044
- };
3554
+ return replacementLiteral(left, value, type);
3045
3555
  }
3046
3556
  break;
3047
3557
  }
@@ -3051,7 +3561,8 @@ function optimizeNode(state, node) {
3051
3561
  break;
3052
3562
  const falsy = left.value === false ||
3053
3563
  left.value === null ||
3054
- (left.value === 0 && (left_type === "Number" || left_type === "Long"));
3564
+ ((left_type === "Number" || left_type === "Long") &&
3565
+ (left.value === 0 || left.value === 0n));
3055
3566
  if (falsy === (node.operator === "&&")) {
3056
3567
  return left;
3057
3568
  }
@@ -3092,9 +3603,7 @@ function evaluateFunction(state, func, args) {
3092
3603
  const paramValues = args &&
3093
3604
  Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
3094
3605
  let ret = null;
3095
- const body = args
3096
- ? JSON.parse(JSON.stringify(func.body))
3097
- : func.body;
3606
+ const body = args ? cloneDeep(func.body) : func.body;
3098
3607
  try {
3099
3608
  traverseAst(body, (node) => {
3100
3609
  switch (node.type) {
@@ -3145,12 +3654,10 @@ function markFunctionCalled(state, func) {
3145
3654
  pushUnique(state.calledFunctions[func.id.name], func);
3146
3655
  }
3147
3656
  async function optimizeMonkeyC(fnMap, barrelList, config) {
3148
- const state = {
3149
- ...(await analyze(fnMap, barrelList, config)),
3150
- localsStack: [{}],
3151
- exposed: {},
3152
- calledFunctions: {},
3153
- };
3657
+ const state = (await analyze(fnMap, barrelList, config));
3658
+ state.localsStack = [{}];
3659
+ state.calledFunctions = {};
3660
+ state.usedByName = {};
3154
3661
  const replace = (node, old) => {
3155
3662
  if (node === false || node === null)
3156
3663
  return node;
@@ -3239,6 +3746,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3239
3746
  f.type == "FunctionDeclaration" &&
3240
3747
  maybeCalled(f.node))) ||
3241
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
+ };
3242
3801
  state.pre = (node) => {
3243
3802
  switch (node.type) {
3244
3803
  case "ConditionalExpression":
@@ -3259,7 +3818,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3259
3818
  result = !!value.value;
3260
3819
  }
3261
3820
  if (result !== null) {
3262
- node.test = { type: "Literal", value: result };
3821
+ node.test = {
3822
+ type: "Literal",
3823
+ value: result,
3824
+ raw: result.toString(),
3825
+ };
3263
3826
  if (node.type === "IfStatement" ||
3264
3827
  node.type === "ConditionalExpression") {
3265
3828
  return [result ? "consequent" : "alternate"];
@@ -3278,7 +3841,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3278
3841
  return null;
3279
3842
  }
3280
3843
  case "EnumDeclaration":
3281
- return false;
3844
+ return [];
3282
3845
  case "ForStatement": {
3283
3846
  const map = topLocals().map;
3284
3847
  if (map) {
@@ -3287,43 +3850,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3287
3850
  break;
3288
3851
  }
3289
3852
  case "VariableDeclarator": {
3290
- const locals = topLocals();
3291
- const { map } = locals;
3292
- if (map) {
3293
- const declName = variableDeclarationName(node.id);
3294
- const name = renameVariable(state, locals, declName);
3295
- if (name) {
3296
- if (node.id.type === "Identifier") {
3297
- node.id.name = name;
3298
- }
3299
- else {
3300
- node.id.left.name = name;
3301
- }
3302
- }
3303
- else {
3304
- map[declName] = true;
3305
- }
3306
- }
3853
+ renamer(node.id);
3307
3854
  return ["init"];
3308
3855
  }
3309
3856
  case "CatchClause":
3310
3857
  if (node.param) {
3311
3858
  state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
3312
- const locals = topLocals();
3313
- const map = locals.map;
3314
- const declName = variableDeclarationName(node.param);
3315
- const name = renameVariable(state, locals, declName);
3316
- if (name) {
3317
- if (node.param.type === "Identifier") {
3318
- node.param.name = name;
3319
- }
3320
- else {
3321
- node.param.left.name = name;
3322
- }
3323
- }
3324
- else {
3325
- map[declName] = true;
3326
- }
3859
+ renamer(node.param);
3327
3860
  return ["body"];
3328
3861
  }
3329
3862
  break;
@@ -3339,14 +3872,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3339
3872
  break;
3340
3873
  case "UnaryExpression":
3341
3874
  if (node.operator == ":") {
3342
- // If we produce a Symbol, for a given name,
3343
- // its possible that someone uses that symbol
3344
- // indirectly, so we can't remove any enums or
3345
- // constants with that name (we can still replace
3346
- // uses of those constants though).
3347
- state.exposed[node.argument.name] = true;
3348
- // In any case, we can't replace *this* use of the
3349
- // symbol with its value...
3875
+ // node.argument is not a normal identifier.
3876
+ // don't visit it.
3350
3877
  return [];
3351
3878
  }
3352
3879
  break;
@@ -3358,29 +3885,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3358
3885
  if (typeof name === "string") {
3359
3886
  node.name = name;
3360
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
+ }
3361
3908
  }
3362
3909
  }
3363
3910
  if (hasProperty(state.index, node.name)) {
3364
3911
  if (!lookupAndReplace(node)) {
3365
- state.exposed[node.name] = true;
3912
+ state.usedByName[node.name] = true;
3366
3913
  }
3367
3914
  }
3368
3915
  return [];
3369
3916
  }
3370
- case "MemberExpression":
3371
- if (node.property.type === "Identifier" && !node.computed) {
3372
- 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)) {
3373
3921
  if (lookupAndReplace(node)) {
3374
3922
  return false;
3375
3923
  }
3376
3924
  else {
3377
- state.exposed[node.property.name] = true;
3925
+ state.usedByName[property.name] = true;
3378
3926
  }
3379
3927
  }
3380
3928
  // Don't optimize the property.
3381
3929
  return ["object"];
3382
3930
  }
3383
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
+ }
3384
3955
  case "BlockStatement": {
3385
3956
  const map = topLocals().map;
3386
3957
  if (map) {
@@ -3396,7 +3967,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3396
3967
  node.params &&
3397
3968
  node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
3398
3969
  state.localsStack.push({ node, map });
3399
- 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;
3400
3975
  if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
3401
3976
  let used = false;
3402
3977
  if (node.id.name == "initialize") {
@@ -3423,10 +3998,23 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3423
3998
  return replace(opt, node);
3424
3999
  }
3425
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;
3426
4009
  case "BlockStatement":
3427
4010
  if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
3428
4011
  node.body.splice(0, 1, ...node.body[0].body);
3429
4012
  }
4013
+ // fall through
4014
+ case "ForStatement":
4015
+ if (locals.map) {
4016
+ cleanupUnusedVars(state, node);
4017
+ }
3430
4018
  break;
3431
4019
  case "ConditionalExpression":
3432
4020
  case "IfStatement":
@@ -3459,17 +4047,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3459
4047
  return replace(optimizeCall(state, node.argument, node), node.argument);
3460
4048
  }
3461
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;
3462
4061
  case "CallExpression": {
3463
4062
  return replace(optimizeCall(state, node, null), node);
3464
4063
  }
3465
- case "AssignmentExpression":
3466
- if (node.operator === "=" &&
3467
- node.left.type === "Identifier" &&
3468
- node.right.type === "Identifier" &&
3469
- node.left.name === node.right.name) {
3470
- return { type: "Literal", value: null, raw: "null" };
3471
- }
3472
- break;
3473
4064
  case "VariableDeclaration": {
3474
4065
  const locals = topLocals();
3475
4066
  if (locals.map &&
@@ -3543,6 +4134,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3543
4134
  }
3544
4135
  }
3545
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;
3546
4163
  }
3547
4164
  return null;
3548
4165
  };
@@ -3550,13 +4167,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3550
4167
  collectNamespaces(f.ast, state);
3551
4168
  });
3552
4169
  state.calledFunctions = {};
3553
- state.exposed = {};
4170
+ state.exposed = state.nextExposed;
4171
+ state.nextExposed = {};
3554
4172
  Object.values(fnMap).forEach((f) => {
3555
4173
  collectNamespaces(f.ast, state);
3556
4174
  });
4175
+ state.exposed = state.nextExposed;
4176
+ state.nextExposed = {};
3557
4177
  delete state.pre;
3558
4178
  delete state.post;
3559
- state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
4179
+ Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
3560
4180
  const cleanup = (node) => {
3561
4181
  switch (node.type) {
3562
4182
  case "ThisExpression":
@@ -3566,7 +4186,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3566
4186
  if (node.members.every((m) => {
3567
4187
  const name = "name" in m ? m.name : m.id.name;
3568
4188
  return (hasProperty(state.index, name) &&
3569
- !hasProperty(state.exposed, name));
4189
+ !hasProperty(state.exposed, name) &&
4190
+ !hasProperty(state.usedByName, name));
3570
4191
  })) {
3571
4192
  node.enumType = [
3572
4193
  ...new Set(node.members.map((m) => {
@@ -3609,7 +4230,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3609
4230
  case "VariableDeclaration": {
3610
4231
  node.declarations = node.declarations.filter((d) => {
3611
4232
  const name = variableDeclarationName(d.id);
3612
- 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));
3613
4236
  });
3614
4237
  if (!node.declarations.length) {
3615
4238
  return false;
@@ -3642,7 +4265,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3642
4265
  }
3643
4266
  return null;
3644
4267
  };
3645
- Object.values(fnMap).forEach((f) => {
4268
+ Object.entries(fnMap).forEach(([name, f]) => {
3646
4269
  traverseAst(f.ast, undefined, (node) => {
3647
4270
  const ret = cleanup(node);
3648
4271
  if (ret === false) {
@@ -3650,16 +4273,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3650
4273
  }
3651
4274
  return ret;
3652
4275
  });
4276
+ if (state.config && state.config.checkBuildPragmas) {
4277
+ pragmaChecker(state, f.ast, state.diagnostics?.[name]);
4278
+ }
3653
4279
  });
3654
4280
  return state.diagnostics;
3655
4281
  }
3656
4282
  function optimizeCall(state, node, context) {
3657
4283
  const [name, results] = state.lookupNonlocal(node.callee);
3658
- const callees = results &&
3659
- results
3660
- .map((r) => r.results)
3661
- .flat()
3662
- .filter((c) => c.type === "FunctionDeclaration");
4284
+ const callees = results ? findCallees(results) : null;
3663
4285
  if (!callees || !callees.length) {
3664
4286
  const n = name ||
3665
4287
  ("name" in node.callee && node.callee.name) ||
@@ -3668,14 +4290,24 @@ function optimizeCall(state, node, context) {
3668
4290
  "name" in node.callee.property &&
3669
4291
  node.callee.property.name);
3670
4292
  if (n) {
3671
- 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
+ }
3672
4299
  }
3673
- else {
3674
- // There are unnamed CallExpressions, such as new [size]
3675
- // 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);
3676
4305
  }
3677
4306
  return null;
3678
4307
  }
4308
+ if (state.currentFunction) {
4309
+ recordCalledFuncs(state.currentFunction, callees);
4310
+ }
3679
4311
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
3680
4312
  const callee = callees[0].node;
3681
4313
  if (!context &&
@@ -3886,20 +4518,18 @@ function checkOne(state, ns, decls, node, isStatic) {
3886
4518
  return next ? (result ? result.concat(next) : next) : result;
3887
4519
  }, null);
3888
4520
  };
3889
- if (api_isStateNode(ns)) {
3890
- const ndecls = ns[decls];
3891
- if (ast_hasProperty(ndecls, node.name)) {
3892
- return ndecls[node.name];
3893
- }
3894
- switch (ns.type) {
3895
- case "ClassDeclaration":
3896
- if (!isStatic) {
3897
- return superChain(ns) || superChainScopes(ns) || false;
3898
- }
3899
- // fall through
3900
- case "ModuleDeclaration":
3901
- return lookupInContext(ns) || false;
3902
- }
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;
3903
4533
  }
3904
4534
  return null;
3905
4535
  }
@@ -3922,6 +4552,13 @@ function sameLookupDefinition(a, b) {
3922
4552
  function api_sameLookupResult(a, b) {
3923
4553
  return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
3924
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
+ }
3925
4562
  /**
3926
4563
  *
3927
4564
  * @param state - The ProgramState
@@ -3942,9 +4579,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
3942
4579
  const stack = maybeStack || state.stack;
3943
4580
  switch (node.type) {
3944
4581
  case "MemberExpression": {
3945
- if (node.property.type != "Identifier" || node.computed)
4582
+ const property = api_isLookupCandidate(node);
4583
+ if (!property)
3946
4584
  break;
3947
- const property = node.property;
3948
4585
  let result;
3949
4586
  if (node.object.type === "ThisExpression") {
3950
4587
  [, result] = lookup(state, decls, node.property, name, stack, true);
@@ -3958,6 +4595,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
3958
4595
  result = results.reduce((current, lookupDef) => {
3959
4596
  const items = lookupDef.results
3960
4597
  .map((module) => {
4598
+ if (!api_isStateNode(module)) {
4599
+ return null;
4600
+ }
3961
4601
  const res = checkOne(state, module, decls, property, false);
3962
4602
  return res ? { parent: module, results: res } : null;
3963
4603
  })
@@ -4042,6 +4682,8 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4042
4682
  }
4043
4683
  function api_collectNamespaces(ast, stateIn) {
4044
4684
  const state = (stateIn || {});
4685
+ if (!state.nextExposed)
4686
+ state.nextExposed = {};
4045
4687
  if (!state.index)
4046
4688
  state.index = {};
4047
4689
  if (!state.stack) {
@@ -4077,7 +4719,7 @@ function api_collectNamespaces(ast, stateIn) {
4077
4719
  state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
4078
4720
  ? { ...elm }
4079
4721
  : elm);
4080
- state.inType = false;
4722
+ state.inType = 0;
4081
4723
  state.traverse = (root) => ast_traverseAst(root, (node) => {
4082
4724
  try {
4083
4725
  if (state.shouldExclude && state.shouldExclude(node)) {
@@ -4085,6 +4727,13 @@ function api_collectNamespaces(ast, stateIn) {
4085
4727
  return [];
4086
4728
  }
4087
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 [];
4088
4737
  case "Program":
4089
4738
  if (state.stack.length != 1) {
4090
4739
  throw new Error("Unexpected stack length for Program node");
@@ -4093,7 +4742,7 @@ function api_collectNamespaces(ast, stateIn) {
4093
4742
  break;
4094
4743
  case "TypeSpecList":
4095
4744
  case "TypeSpecPart":
4096
- state.inType = true;
4745
+ state.inType++;
4097
4746
  break;
4098
4747
  case "ImportModule":
4099
4748
  case "Using": {
@@ -4139,6 +4788,16 @@ function api_collectNamespaces(ast, stateIn) {
4139
4788
  });
4140
4789
  }
4141
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;
4142
4801
  case "BlockStatement": {
4143
4802
  const [parent] = state.stack.slice(-1);
4144
4803
  if (parent.node === node ||
@@ -4205,15 +4864,17 @@ function api_collectNamespaces(ast, stateIn) {
4205
4864
  // an EnumDeclaration doesn't create a scope, but
4206
4865
  // it does create a type (if it has a name)
4207
4866
  case "EnumDeclaration": {
4208
- if (!node.id)
4867
+ if (!node.id) {
4868
+ state.inType++;
4209
4869
  break;
4870
+ }
4210
4871
  const [parent] = state.stack.slice(-1);
4211
4872
  const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
4212
4873
  node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
4213
4874
  }
4214
4875
  // fall through
4215
4876
  case "TypedefDeclaration": {
4216
- state.inType = true;
4877
+ state.inType++;
4217
4878
  const name = node.id.name;
4218
4879
  const [parent] = state.stack.slice(-1);
4219
4880
  if (!parent.type_decls)
@@ -4266,13 +4927,21 @@ function api_collectNamespaces(ast, stateIn) {
4266
4927
  break;
4267
4928
  }
4268
4929
  case "EnumStringBody": {
4269
- 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--;
4270
4934
  const [parent] = state.stack.slice(-1);
4271
4935
  const values = parent.decls || (parent.decls = {});
4272
4936
  let prev = -1;
4273
4937
  node.members.forEach((m, i) => {
4274
4938
  if (m.type == "Identifier") {
4275
- prev += 1;
4939
+ if (typeof prev === "bigint") {
4940
+ prev += 1n;
4941
+ }
4942
+ else {
4943
+ prev += 1;
4944
+ }
4276
4945
  m = node.members[i] = {
4277
4946
  type: "EnumStringMember",
4278
4947
  loc: m.loc,
@@ -4282,7 +4951,7 @@ function api_collectNamespaces(ast, stateIn) {
4282
4951
  init: {
4283
4952
  type: "Literal",
4284
4953
  value: prev,
4285
- raw: prev.toString(),
4954
+ raw: prev.toString() + (typeof prev === "bigint" ? "l" : ""),
4286
4955
  enumType: m.enumType,
4287
4956
  loc: m.loc,
4288
4957
  start: m.start,
@@ -4334,22 +5003,22 @@ function api_collectNamespaces(ast, stateIn) {
4334
5003
  if (state.post)
4335
5004
  ret = state.post(node, state);
4336
5005
  switch (type) {
4337
- // Don't clear inType for TypeSpecPart, since they
4338
- // generally occur in TypeSpecLists. But do clear it for
4339
- // SizedArrayExpression, since thats the only place they
4340
- // happen on their own.
4341
- case "SizedArrayExpression":
5006
+ case "TypeSpecPart":
4342
5007
  case "TypeSpecList":
4343
5008
  case "TypedefDeclaration":
4344
5009
  case "EnumDeclaration":
4345
- state.inType = false;
5010
+ state.inType--;
4346
5011
  break;
4347
5012
  case "EnumStringBody":
4348
- state.inType = true;
5013
+ state.inType++;
4349
5014
  break;
4350
5015
  }
4351
5016
  const [parent] = state.stack.slice(-1);
4352
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)
4353
5022
  (node.type === "CatchClause" && parent.node === node.body)) {
4354
5023
  delete parent.usings;
4355
5024
  delete parent.imports;
@@ -4368,6 +5037,9 @@ function api_collectNamespaces(ast, stateIn) {
4368
5037
  }
4369
5038
  });
4370
5039
  state.traverse(ast);
5040
+ if (state.inType) {
5041
+ throw new Error(`inType was non-zero on exit: ${state.inType}`);
5042
+ }
4371
5043
  if (state.stack.length != 1) {
4372
5044
  throw new Error("Invalid AST!");
4373
5045
  }
@@ -4402,7 +5074,7 @@ function api_formatAst(node, monkeyCSource = null) {
4402
5074
  // json. The parser knows to just treat the last line of the input
4403
5075
  // as the ast itself, and the printers will find what they're
4404
5076
  // looking for in the source.
4405
- const source = (monkeyCSource || "") + "\n" + JSON.stringify(node);
5077
+ const source = (monkeyCSource || "") + "\n" + (0,prettier_plugin_monkeyc_namespaceObject.serializeMonkeyC)(node);
4406
5078
  return external_prettier_namespaceObject.format(source, {
4407
5079
  parser: "monkeyc-json",
4408
5080
  plugins: [(prettier_plugin_monkeyc_default())],
@@ -4489,6 +5161,36 @@ function findUsingForNode(state, stack, i, node, isType) {
4489
5161
  }
4490
5162
  return null;
4491
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
+ }
4492
5194
 
4493
5195
  })();
4494
5196