@markw65/monkeyc-optimizer 1.0.19 → 1.0.22

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,formatAst,getApiMapping,hasProperty,isStateNode,traverseAst,variableDeclarationName});
1
+ 0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiMapping,hasProperty,isStateNode,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
2
2
  /******/ (() => { // webpackBootstrap
3
3
  /******/ "use strict";
4
4
  /******/ // The require scope
@@ -53,12 +53,15 @@ __webpack_require__.r(__webpack_exports__);
53
53
  // EXPORTS
54
54
  __webpack_require__.d(__webpack_exports__, {
55
55
  "collectNamespaces": () => (/* binding */ api_collectNamespaces),
56
- "formatAst": () => (/* binding */ formatAst),
56
+ "findUsingForNode": () => (/* binding */ findUsingForNode),
57
+ "formatAst": () => (/* binding */ api_formatAst),
57
58
  "getApiMapping": () => (/* binding */ api_getApiMapping),
58
59
  "hasProperty": () => (/* binding */ api_hasProperty),
59
60
  "isStateNode": () => (/* binding */ api_isStateNode),
61
+ "sameLookupResult": () => (/* binding */ api_sameLookupResult),
60
62
  "traverseAst": () => (/* binding */ api_traverseAst),
61
- "variableDeclarationName": () => (/* binding */ api_variableDeclarationName)
63
+ "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
64
+ "visitReferences": () => (/* reexport */ visitor_visitReferences)
62
65
  });
63
66
 
64
67
  ;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
@@ -132,23 +135,32 @@ function getArgSafety(state, func, args, requireAll) {
132
135
  // determine whether decl might be changed by a function call
133
136
  // or assignment during the evaluation of FunctionStateNode.
134
137
  const getSafety = (decl) => {
135
- // enums are constant, they cant change
136
- if (decl.type === "EnumStringMember")
137
- return true;
138
- if (decl.type === "VariableDeclarator") {
139
- // constants also can't change
140
- if (decl.node.kind === "const")
138
+ switch (decl.type) {
139
+ // enums are constant, they cant change
140
+ case "EnumStringMember":
141
141
  return true;
142
- // if decl is a local, it also can't be changed
143
- // by a call to another function.
144
- for (let i = 0;; i++) {
145
- if (!state.stack[i] || decl.stack[i] !== state.stack[i])
146
- return false;
147
- if (state.stack[i].type === "FunctionDeclaration")
142
+ case "VariableDeclarator": {
143
+ // constants also can't change
144
+ if (decl.node.kind === "const")
148
145
  return true;
146
+ // if decl is a local, it also can't be changed
147
+ // by a call to another function.
148
+ for (let i = 0;; i++) {
149
+ if (!state.stack[i] || decl.stack[i] !== state.stack[i])
150
+ return false;
151
+ if (state.stack[i].type === "FunctionDeclaration")
152
+ return true;
153
+ }
149
154
  }
155
+ case "Identifier":
156
+ case "BinaryExpression":
157
+ // This is a parameter of the calling function.
158
+ // It also can't be changed during the execution
159
+ // of the inlined function
160
+ return true;
161
+ default:
162
+ return null;
150
163
  }
151
- return null;
152
164
  };
153
165
  const safeArgs = [];
154
166
  let allSafe = true;
@@ -160,11 +172,13 @@ function getArgSafety(state, func, args, requireAll) {
160
172
  case "Identifier":
161
173
  case "MemberExpression": {
162
174
  const [, results] = state.lookup(arg);
163
- if (!results || results.length !== 1) {
175
+ if (!results ||
176
+ results.length !== 1 ||
177
+ results[0].results.length !== 1) {
164
178
  safeArgs.push(null);
165
179
  return !requireAll;
166
180
  }
167
- const safety = getSafety(results[0]);
181
+ const safety = getSafety(results[0].results[0]);
168
182
  safeArgs.push(safety);
169
183
  if (!safety) {
170
184
  allSafe = false;
@@ -256,27 +270,23 @@ function inliningLooksUseful(func, node) {
256
270
  }
257
271
  return false;
258
272
  }
259
- var InlineStatus;
260
- (function (InlineStatus) {
261
- InlineStatus[InlineStatus["Never"] = 0] = "Never";
262
- InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
263
- InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
264
- })(InlineStatus || (InlineStatus = {}));
265
273
  function inlineRequested(state, func) {
266
274
  const excludeAnnotations = (func.node.loc?.source &&
267
275
  state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
268
276
  {};
269
277
  if (func.node.attrs &&
270
- func.node.attrs.attrs &&
271
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
278
+ func.node.attrs.attributes &&
279
+ func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
272
280
  (attr.argument.name === "inline" ||
273
281
  (attr.argument.name.startsWith("inline_") &&
274
- hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
282
+ !hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
275
283
  return true;
276
284
  }
277
285
  return false;
278
286
  }
279
287
  function inliner_shouldInline(state, func, call, context) {
288
+ if (state.inlining)
289
+ return false;
280
290
  let autoInline = false;
281
291
  let inlineAsExpression = false;
282
292
  const args = call.arguments;
@@ -309,6 +319,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
309
319
  let failed = false;
310
320
  const pre = state.pre;
311
321
  const post = state.post;
322
+ state.inlining = true;
312
323
  try {
313
324
  state.pre = (node) => {
314
325
  if (failed)
@@ -354,7 +365,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
354
365
  };
355
366
  state.post = (node) => {
356
367
  if (failed)
357
- return null;
368
+ return post(node, state);
358
369
  let replacement = null;
359
370
  switch (node.type) {
360
371
  case "Identifier": {
@@ -371,12 +382,13 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
371
382
  if (!replacement) {
372
383
  failed = true;
373
384
  inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
374
- return null;
385
+ return post(node, state);
375
386
  }
376
387
  break;
377
388
  }
378
389
  }
379
- return post(replacement || node, state) || replacement;
390
+ const ret = post(replacement || node, state);
391
+ return ret === false || ret ? ret : replacement;
380
392
  };
381
393
  let ret = state.traverse(root);
382
394
  if (failed) {
@@ -395,6 +407,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
395
407
  finally {
396
408
  state.pre = pre;
397
409
  state.post = post;
410
+ delete state.inlining;
398
411
  }
399
412
  }
400
413
  function inliner_unused(expression, top) {
@@ -436,13 +449,13 @@ function inliner_unused(expression, top) {
436
449
  },
437
450
  ];
438
451
  }
439
- function diagnostic(state, loc, message) {
452
+ function inliner_diagnostic(state, loc, message, type = "INFO") {
440
453
  if (!loc || !loc.source)
441
454
  return;
442
455
  const source = loc.source;
443
456
  if (!state.diagnostics)
444
457
  state.diagnostics = {};
445
- if (!hasProperty(state.diagnostics, source)) {
458
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
446
459
  if (!message)
447
460
  return;
448
461
  state.diagnostics[source] = [];
@@ -452,7 +465,7 @@ function diagnostic(state, loc, message) {
452
465
  if (message) {
453
466
  if (index < 0)
454
467
  index = diags.length;
455
- diags[index] = { type: "INFO", loc, message };
468
+ diags[index] = { type, loc, message };
456
469
  }
457
470
  else if (index >= 0) {
458
471
  diags.splice(index, 1);
@@ -460,7 +473,7 @@ function diagnostic(state, loc, message) {
460
473
  }
461
474
  function inlineDiagnostic(state, func, call, message) {
462
475
  if (inlineRequested(state, func)) {
463
- diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
476
+ inliner_diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
464
477
  }
465
478
  }
466
479
  function inlineWithArgs(state, func, call, context) {
@@ -508,7 +521,7 @@ function inlineWithArgs(state, func, call, context) {
508
521
  if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
509
522
  return null;
510
523
  }
511
- diagnostic(state, call.loc, null);
524
+ inliner_diagnostic(state, call.loc, null);
512
525
  if (context.type !== "ReturnStatement" && retStmtCount) {
513
526
  const last = body.body[body.body.length - 1];
514
527
  if (last.type != "ReturnStatement") {
@@ -571,23 +584,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
571
584
  // With a bit more work, we could find the guaranteed shortest
572
585
  // reference, and then use this to optimize *all* symbols, not
573
586
  // just fix inlined ones.
574
- if (current &&
575
- current.length === original.length &&
576
- current.every((item, index) => item == original[index])) {
587
+ if (current && sameLookupResult(original, current)) {
577
588
  return lookupNode;
578
589
  }
579
590
  const node = lookupNode.type === "Identifier"
580
591
  ? lookupNode
581
592
  : lookupNode.property;
582
- if (original.length === 1 && original[0].type === "EnumStringMember") {
583
- return applyTypeIfNeeded(original[0].init);
593
+ if (original.length === 1 &&
594
+ original[0].results.length === 1 &&
595
+ original[0].results[0].type === "EnumStringMember") {
596
+ return applyTypeIfNeeded(original[0].results[0].init);
584
597
  }
585
- const prefixes = original.map((sn) => {
598
+ const prefixes = original
599
+ .map((lookupDef) => lookupDef.results.map((sn) => {
586
600
  if (isStateNode(sn) && sn.fullName) {
587
601
  return sn.fullName;
588
602
  }
589
603
  return "";
590
- });
604
+ }))
605
+ .flat();
591
606
  if (prefixes.length &&
592
607
  prefixes[0].startsWith("$.") &&
593
608
  prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
@@ -597,9 +612,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
597
612
  if (found)
598
613
  return current;
599
614
  const [, results] = state.lookup(current);
600
- if (results &&
601
- results.length === original.length &&
602
- results.every((result, i) => result === original[i])) {
615
+ if (results && sameLookupResult(original, results)) {
603
616
  found = true;
604
617
  return current;
605
618
  }
@@ -641,6 +654,85 @@ function fixNodeScope(state, lookupNode, nodeStack) {
641
654
 
642
655
  ;// CONCATENATED MODULE: external "./util.cjs"
643
656
  const external_util_cjs_namespaceObject = require("./util.cjs");
657
+ ;// CONCATENATED MODULE: ./src/visitor.ts
658
+
659
+ function visitor_visitReferences(state, ast, name, defn, callback) {
660
+ const checkResults = ([name, results], node) => {
661
+ if (name && results) {
662
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
663
+ if (callback(node, results, false) === false) {
664
+ return [];
665
+ }
666
+ }
667
+ }
668
+ else if (defn === false) {
669
+ if (callback(node, [], results === null) === false) {
670
+ return [];
671
+ }
672
+ }
673
+ return null;
674
+ };
675
+ state.pre = (node) => {
676
+ switch (node.type) {
677
+ case "AttributeList":
678
+ return [];
679
+ case "UnaryExpression":
680
+ // a bare symbol isn't a reference
681
+ if (node.operator === ":")
682
+ return [];
683
+ break;
684
+ case "BinaryExpression":
685
+ /*
686
+ * `expr has :symbol` can be treated as a reference
687
+ * to expr.symbol.
688
+ */
689
+ if (node.operator === "has") {
690
+ if (node.right.type === "UnaryExpression" &&
691
+ node.right.operator === ":") {
692
+ if (!name || node.right.argument.name === name) {
693
+ return checkResults(state.lookup({
694
+ type: "MemberExpression",
695
+ object: node.left,
696
+ property: node.right.argument,
697
+ computed: false,
698
+ }), node.right.argument);
699
+ }
700
+ }
701
+ }
702
+ break;
703
+ case "CallExpression":
704
+ // A call expression whose callee is an identifier is looked
705
+ // up as a non-local. ie even if there's a same named local,
706
+ // it will be ignored, and the lookup will start as if the
707
+ // call had been written self.foo() rather than foo().
708
+ if (node.callee.type === "Identifier") {
709
+ if (!name || node.callee.name === name) {
710
+ /* ignore return value */
711
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
712
+ }
713
+ return ["arguments"];
714
+ }
715
+ break;
716
+ case "Identifier":
717
+ if (!name || node.name === name) {
718
+ return checkResults(state.lookup(node), node);
719
+ }
720
+ break;
721
+ case "MemberExpression":
722
+ if (!node.computed && node.property.type === "Identifier") {
723
+ if (!name || node.property.name === name) {
724
+ return checkResults(state.lookup(node), node) || ["object"];
725
+ }
726
+ return ["object"];
727
+ }
728
+ break;
729
+ }
730
+ return null;
731
+ };
732
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
733
+ delete state.pre;
734
+ }
735
+
644
736
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
645
737
 
646
738
 
@@ -648,43 +740,24 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
648
740
 
649
741
 
650
742
 
651
- function processImports(allImports, lookup) {
652
- allImports.forEach(({ node, stack }) => {
653
- const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
654
- if (name && module) {
655
- const [parent] = stack.slice(-1);
656
- if (!parent.decls)
657
- parent.decls = {};
658
- const decls = parent.decls;
659
- if (!hasProperty(decls, name))
660
- decls[name] = [];
661
- module.forEach((m) => {
662
- if (isStateNode(m) && m.type == "ModuleDeclaration") {
663
- pushUnique(decls[name], m);
664
- if (!parent.type_decls)
665
- parent.type_decls = {};
666
- const tdecls = parent.type_decls;
667
- if (!hasProperty(tdecls, name))
668
- tdecls[name] = [];
669
- pushUnique(tdecls[name], m);
670
- if (node.type == "ImportModule" && m.type_decls) {
671
- Object.entries(m.type_decls).forEach(([name, decls]) => {
672
- if (!hasProperty(tdecls, name))
673
- tdecls[name] = [];
674
- decls.forEach((decl) => pushUnique(tdecls[name], decl));
675
- });
676
- }
677
- }
678
- });
679
- }
680
- });
681
- }
743
+
682
744
  function collectClassInfo(state) {
745
+ const toybox = state.stack[0].decls["Toybox"][0];
746
+ const lang = toybox.decls["Lang"][0];
747
+ const object = lang.decls["Object"];
683
748
  state.allClasses.forEach((elm) => {
749
+ if (elm.stack[elm.stack.length - 1].type === "ClassDeclaration") {
750
+ // nested classes don't get access to their contained
751
+ // context. Put them in the global scope instead.
752
+ elm.stack = elm.stack.slice(0, 1);
753
+ }
684
754
  if (elm.node.superClass) {
685
- const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
686
- const superClass = classes &&
687
- classes.filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
755
+ const [name, lookupDefns] = state.lookup(elm.node.superClass, null, elm.stack);
756
+ const superClass = lookupDefns &&
757
+ lookupDefns
758
+ .map((lookupDefn) => lookupDefn.results)
759
+ .flat()
760
+ .filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
688
761
  // set it "true" if there is a superClass, but we can't find it.
689
762
  elm.superClass = superClass && superClass.length ? superClass : true;
690
763
  if (name && elm.superClass !== true) {
@@ -716,6 +789,9 @@ function collectClassInfo(state) {
716
789
  elm.decls[name] = elm.superClass;
717
790
  }
718
791
  }
792
+ else if (elm !== object[0]) {
793
+ elm.superClass = object;
794
+ }
719
795
  });
720
796
  const markOverrides = (cls, scls) => {
721
797
  if (scls === true)
@@ -769,22 +845,23 @@ function getFileASTs(fnMap) {
769
845
  return ok;
770
846
  }, true));
771
847
  }
772
- async function analyze(fnMap) {
848
+ async function analyze(fnMap, barrelList, config) {
773
849
  let hasTests = false;
774
- const allImports = [];
850
+ let markApi = true;
775
851
  const preState = {
776
852
  fnMap,
853
+ config,
777
854
  allFunctions: [],
778
855
  allClasses: [],
779
856
  shouldExclude(node) {
780
857
  if ("attrs" in node &&
781
858
  node.attrs &&
782
- "attrs" in node.attrs &&
783
- node.attrs.attrs &&
859
+ "attributes" in node.attrs &&
860
+ node.attrs.attributes &&
784
861
  node.loc?.source) {
785
862
  const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
786
863
  if (excludeAnnotations) {
787
- return node.attrs.attrs.reduce((drop, attr) => {
864
+ return node.attrs.attributes.elements.reduce((drop, attr) => {
788
865
  if (attr.type != "UnaryExpression")
789
866
  return drop;
790
867
  if (attr.argument.type != "Identifier")
@@ -801,45 +878,37 @@ async function analyze(fnMap) {
801
878
  }
802
879
  return false;
803
880
  },
804
- post(node, state) {
881
+ pre(node, state) {
805
882
  switch (node.type) {
806
883
  case "FunctionDeclaration":
884
+ if (markApi) {
885
+ node.body = null;
886
+ break;
887
+ }
888
+ case "ModuleDeclaration":
807
889
  case "ClassDeclaration": {
808
890
  const [scope] = state.stack.slice(-1);
809
- const stack = state.stack.slice(0, -1);
810
- scope.stack = stack;
891
+ scope.stack = state.stackClone().slice(0, -1);
811
892
  if (scope.type == "FunctionDeclaration") {
893
+ scope.isStatic =
894
+ scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
895
+ (scope.node.attrs &&
896
+ scope.node.attrs.access &&
897
+ scope.node.attrs.access.includes("static"));
812
898
  state.allFunctions.push(scope);
813
899
  }
814
- else {
900
+ else if (scope.type === "ClassDeclaration") {
815
901
  state.allClasses.push(scope);
816
902
  }
817
- return null;
903
+ break;
818
904
  }
819
- case "Using":
820
- case "ImportModule":
821
- allImports.push({ node, stack: state.stack.slice() });
822
- return null;
823
- default:
824
- return null;
825
905
  }
906
+ return null;
826
907
  },
827
908
  };
828
- await getApiMapping(preState);
909
+ await getApiMapping(preState, barrelList);
910
+ markApi = false;
829
911
  const state = preState;
830
- // Mark all functions from api.mir as "special" by
831
- // setting their bodies to null. In api.mir, they're
832
- // all empty, which makes it look like they're
833
- // do-nothing functions.
834
- const markApi = (node) => {
835
- if (node.type == "FunctionDeclaration") {
836
- node.node.body = null;
837
- }
838
- if (isStateNode(node) && node.decls) {
839
- Object.values(node.decls).forEach((v) => v.forEach(markApi));
840
- }
841
- };
842
- markApi(state.stack[0]);
843
912
  await getFileASTs(fnMap);
844
913
  Object.entries(fnMap).forEach(([name, value]) => {
845
914
  const { ast, parserError } = value;
@@ -852,8 +921,28 @@ async function analyze(fnMap) {
852
921
  });
853
922
  delete state.shouldExclude;
854
923
  delete state.post;
855
- processImports(allImports, state.lookup);
856
924
  collectClassInfo(state);
925
+ const diagnosticType = config?.checkInvalidSymbols !== "OFF"
926
+ ? config?.checkInvalidSymbols || "WARNING"
927
+ : null;
928
+ if (diagnosticType &&
929
+ !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
930
+ const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
931
+ Object.entries(fnMap).forEach(([k, v]) => {
932
+ visitReferences(state, v.ast, null, false, (node, results, error) => {
933
+ if (!error)
934
+ return undefined;
935
+ const nodeStr = formatAst(node);
936
+ if (state.inType) {
937
+ if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
938
+ return undefined;
939
+ }
940
+ }
941
+ diagnostic(state, node.loc, `Undefined symbol ${nodeStr}`, diagnosticType);
942
+ return false;
943
+ });
944
+ });
945
+ }
857
946
  return state;
858
947
  }
859
948
  function compareLiteralLike(a, b) {
@@ -863,11 +952,11 @@ function compareLiteralLike(a, b) {
863
952
  b = b.left;
864
953
  return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
865
954
  }
866
- function getLiteralFromDecls(decls) {
867
- if (!decls.length)
955
+ function getLiteralFromDecls(lookupDefns) {
956
+ if (!lookupDefns.length)
868
957
  return null;
869
958
  let result = null;
870
- if (decls.every((d) => {
959
+ if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
871
960
  if (d.type === "EnumStringMember" ||
872
961
  (d.type === "VariableDeclarator" && d.node.kind === "const")) {
873
962
  const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
@@ -882,7 +971,7 @@ function getLiteralFromDecls(decls) {
882
971
  }
883
972
  }
884
973
  return false;
885
- })) {
974
+ }))) {
886
975
  return result;
887
976
  }
888
977
  return null;
@@ -1086,14 +1175,30 @@ function evaluateFunction(func, args) {
1086
1175
  return false;
1087
1176
  }
1088
1177
  }
1089
- async function optimizeMonkeyC(fnMap) {
1178
+ function markFunctionCalled(state, func) {
1179
+ if (!hasProperty(state.calledFunctions, func.id.name)) {
1180
+ state.calledFunctions[func.id.name] = [func];
1181
+ return;
1182
+ }
1183
+ pushUnique(state.calledFunctions[func.id.name], func);
1184
+ }
1185
+ async function optimizeMonkeyC(fnMap, barrelList, config) {
1090
1186
  const state = {
1091
- ...(await analyze(fnMap)),
1187
+ ...(await analyze(fnMap, barrelList, config)),
1092
1188
  localsStack: [{}],
1093
1189
  exposed: {},
1094
1190
  calledFunctions: {},
1095
1191
  };
1096
- const replace = (node, obj) => {
1192
+ const replace = (node, old) => {
1193
+ if (node === false || node === null)
1194
+ return node;
1195
+ const rep = state.traverse(node);
1196
+ if (rep === false || Array.isArray(rep))
1197
+ return rep;
1198
+ return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
1199
+ };
1200
+ const inPlaceReplacement = (node, obj) => {
1201
+ const { start, end, loc } = node;
1097
1202
  for (const k of Object.keys(node)) {
1098
1203
  delete node[k];
1099
1204
  }
@@ -1101,13 +1206,16 @@ async function optimizeMonkeyC(fnMap) {
1101
1206
  obj = {
1102
1207
  type: "BinaryExpression",
1103
1208
  operator: "as",
1104
- left: obj,
1209
+ left: { ...obj, start, end, loc },
1105
1210
  right: { type: "TypeSpecList", ts: [obj.enumType] },
1106
1211
  };
1107
1212
  }
1108
1213
  for (const [k, v] of Object.entries(obj)) {
1109
1214
  node[k] = v;
1110
1215
  }
1216
+ node.loc = loc;
1217
+ node.start = start;
1218
+ node.end = end;
1111
1219
  };
1112
1220
  const lookupAndReplace = (node) => {
1113
1221
  const [, objects] = state.lookup(node);
@@ -1118,7 +1226,7 @@ async function optimizeMonkeyC(fnMap) {
1118
1226
  if (!obj) {
1119
1227
  return false;
1120
1228
  }
1121
- replace(node, obj);
1229
+ inPlaceReplacement(node, obj);
1122
1230
  return true;
1123
1231
  };
1124
1232
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
@@ -1134,8 +1242,8 @@ async function optimizeMonkeyC(fnMap) {
1134
1242
  if (hasProperty(state.exposed, func.id.name))
1135
1243
  return true;
1136
1244
  if (func.attrs &&
1137
- func.attrs.attrs &&
1138
- func.attrs.attrs.some((attr) => {
1245
+ func.attrs.attributes &&
1246
+ func.attrs.attributes.elements.some((attr) => {
1139
1247
  if (attr.type != "UnaryExpression")
1140
1248
  return false;
1141
1249
  if (attr.argument.type != "Identifier")
@@ -1226,6 +1334,37 @@ async function optimizeMonkeyC(fnMap) {
1226
1334
  }
1227
1335
  return ["init"];
1228
1336
  }
1337
+ case "CatchClause":
1338
+ if (node.param) {
1339
+ state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
1340
+ const locals = topLocals();
1341
+ const map = locals.map;
1342
+ const declName = variableDeclarationName(node.param);
1343
+ const name = renameVariable(state, locals, declName);
1344
+ if (name) {
1345
+ if (node.param.type === "Identifier") {
1346
+ node.param.name = name;
1347
+ }
1348
+ else {
1349
+ node.param.left.name = name;
1350
+ }
1351
+ }
1352
+ else {
1353
+ map[declName] = true;
1354
+ }
1355
+ return ["body"];
1356
+ }
1357
+ break;
1358
+ case "BinaryExpression":
1359
+ if (node.operator === "has") {
1360
+ if (node.right.type === "UnaryExpression" &&
1361
+ node.right.operator === ":") {
1362
+ // Using `expr has :symbol` doesn't "expose"
1363
+ // symbol. So skip the right operand.
1364
+ return ["left"];
1365
+ }
1366
+ }
1367
+ break;
1229
1368
  case "UnaryExpression":
1230
1369
  if (node.operator == ":") {
1231
1370
  // If we produce a Symbol, for a given name,
@@ -1295,10 +1434,7 @@ async function optimizeMonkeyC(fnMap) {
1295
1434
  used = checkInherited(parent, node.id.name);
1296
1435
  }
1297
1436
  if (used) {
1298
- if (!hasProperty(state.calledFunctions, node.id.name)) {
1299
- state.calledFunctions[node.id.name] = [];
1300
- }
1301
- state.calledFunctions[node.id.name].push(node);
1437
+ markFunctionCalled(state, node);
1302
1438
  }
1303
1439
  }
1304
1440
  }
@@ -1311,8 +1447,7 @@ async function optimizeMonkeyC(fnMap) {
1311
1447
  }
1312
1448
  const opt = optimizeNode(node);
1313
1449
  if (opt) {
1314
- replace(node, opt);
1315
- return null;
1450
+ return replace(opt, node);
1316
1451
  }
1317
1452
  switch (node.type) {
1318
1453
  case "ConditionalExpression":
@@ -1322,7 +1457,7 @@ async function optimizeMonkeyC(fnMap) {
1322
1457
  const rep = node.test.value ? node.consequent : node.alternate;
1323
1458
  if (!rep)
1324
1459
  return false;
1325
- replace(node, rep);
1460
+ return replace(rep, rep);
1326
1461
  }
1327
1462
  break;
1328
1463
  case "WhileStatement":
@@ -1337,15 +1472,11 @@ async function optimizeMonkeyC(fnMap) {
1337
1472
  break;
1338
1473
  case "ReturnStatement":
1339
1474
  if (node.argument && node.argument.type === "CallExpression") {
1340
- return optimizeCall(state, node.argument, node);
1475
+ return replace(optimizeCall(state, node.argument, node), node.argument);
1341
1476
  }
1342
1477
  break;
1343
1478
  case "CallExpression": {
1344
- const ret = optimizeCall(state, node, null);
1345
- if (ret) {
1346
- replace(node, ret);
1347
- }
1348
- break;
1479
+ return replace(optimizeCall(state, node, null), node);
1349
1480
  }
1350
1481
  case "AssignmentExpression":
1351
1482
  if (node.operator === "=" &&
@@ -1357,7 +1488,7 @@ async function optimizeMonkeyC(fnMap) {
1357
1488
  break;
1358
1489
  case "ExpressionStatement":
1359
1490
  if (node.expression.type === "CallExpression") {
1360
- return optimizeCall(state, node.expression, node);
1491
+ return replace(optimizeCall(state, node.expression, node), node.expression);
1361
1492
  }
1362
1493
  else if (node.expression.type === "AssignmentExpression") {
1363
1494
  if (node.expression.right.type === "CallExpression") {
@@ -1369,21 +1500,20 @@ async function optimizeMonkeyC(fnMap) {
1369
1500
  }
1370
1501
  if (!ok && node.expression.operator == "=") {
1371
1502
  const [, result] = state.lookup(node.expression.left);
1372
- ok = result != null;
1503
+ ok = !!result;
1373
1504
  }
1374
1505
  if (ok) {
1375
- const ret = optimizeCall(state, node.expression.right, node.expression);
1376
- if (ret && ret.type === "BlockStatement") {
1377
- const r2 = state.traverse(ret);
1378
- return r2 === false || r2 ? r2 : ret;
1379
- }
1506
+ return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
1380
1507
  }
1381
1508
  }
1382
1509
  }
1383
1510
  else {
1384
1511
  const ret = unused(node.expression, true);
1385
1512
  if (ret) {
1386
- return ret;
1513
+ return ret
1514
+ .map((r) => replace(r, r))
1515
+ .flat(1)
1516
+ .filter((s) => !!s);
1387
1517
  }
1388
1518
  }
1389
1519
  break;
@@ -1393,10 +1523,18 @@ async function optimizeMonkeyC(fnMap) {
1393
1523
  Object.values(fnMap).forEach((f) => {
1394
1524
  collectNamespaces(f.ast, state);
1395
1525
  });
1526
+ state.calledFunctions = {};
1527
+ state.exposed = {};
1528
+ Object.values(fnMap).forEach((f) => {
1529
+ collectNamespaces(f.ast, state);
1530
+ });
1396
1531
  delete state.pre;
1397
1532
  delete state.post;
1398
1533
  const cleanup = (node) => {
1399
1534
  switch (node.type) {
1535
+ case "ThisExpression":
1536
+ node.text = "self";
1537
+ break;
1400
1538
  case "EnumStringBody":
1401
1539
  if (node.members.every((m) => {
1402
1540
  const name = "name" in m ? m.name : m.id.name;
@@ -1421,19 +1559,24 @@ async function optimizeMonkeyC(fnMap) {
1421
1559
  if (!node.body.members.length) {
1422
1560
  if (!node.id)
1423
1561
  return false;
1424
- if (!node.body["enumType"]) {
1562
+ if (!node.body.enumType) {
1425
1563
  throw new Error("Missing enumType on optimized enum");
1426
1564
  }
1427
- replace(node, {
1565
+ return {
1428
1566
  type: "TypedefDeclaration",
1429
1567
  id: node.id,
1430
1568
  ts: {
1431
1569
  type: "UnaryExpression",
1432
- argument: { type: "TypeSpecList", ts: [node.body.enumType] },
1570
+ argument: {
1571
+ type: "TypeSpecList",
1572
+ ts: [
1573
+ node.body.enumType,
1574
+ ],
1575
+ },
1433
1576
  prefix: true,
1434
1577
  operator: " as",
1435
1578
  },
1436
- });
1579
+ };
1437
1580
  }
1438
1581
  break;
1439
1582
  case "VariableDeclaration": {
@@ -1456,6 +1599,19 @@ async function optimizeMonkeyC(fnMap) {
1456
1599
  return false;
1457
1600
  }
1458
1601
  break;
1602
+ case "ClassDeclaration":
1603
+ case "ModuleDeclaration":
1604
+ // none of the attributes means anything on classes and
1605
+ // modules, and the new compiler complains about some
1606
+ // of them. Just drop them all.
1607
+ if (node.attrs && node.attrs.access) {
1608
+ if (node.attrs.attributes) {
1609
+ delete node.attrs.access;
1610
+ }
1611
+ else {
1612
+ delete node.attrs;
1613
+ }
1614
+ }
1459
1615
  }
1460
1616
  return null;
1461
1617
  };
@@ -1471,7 +1627,12 @@ async function optimizeMonkeyC(fnMap) {
1471
1627
  return state.diagnostics;
1472
1628
  }
1473
1629
  function optimizeCall(state, node, context) {
1474
- const [name, callees] = state.lookup(node.callee);
1630
+ const [name, results] = state.lookupNonlocal(node.callee);
1631
+ const callees = results &&
1632
+ results
1633
+ .map((r) => r.results)
1634
+ .flat()
1635
+ .filter((c) => c.type === "FunctionDeclaration");
1475
1636
  if (!callees || !callees.length) {
1476
1637
  const n = name ||
1477
1638
  ("name" in node.callee && node.callee.name) ||
@@ -1506,10 +1667,7 @@ function optimizeCall(state, node, context) {
1506
1667
  }
1507
1668
  }
1508
1669
  }
1509
- if (!hasProperty(state.calledFunctions, name)) {
1510
- state.calledFunctions[name] = [];
1511
- }
1512
- callees.forEach((c) => isStateNode(c) && pushUnique(state.calledFunctions[name], c.node));
1670
+ callees.forEach((c) => markFunctionCalled(state, c.node));
1513
1671
  return null;
1514
1672
  }
1515
1673
 
@@ -1579,6 +1737,8 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1579
1737
 
1580
1738
 
1581
1739
 
1740
+
1741
+
1582
1742
  /*
1583
1743
  * This is an unfortunate hack. I want to be able to extract things
1584
1744
  * like the types of all of a Class's variables (in particular the type
@@ -1589,16 +1749,30 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1589
1749
  * but those are at least in a standard format.
1590
1750
  */
1591
1751
  // Extract all enum values from api.mir
1592
- async function api_getApiMapping(state) {
1752
+ async function api_getApiMapping(state, barrelList) {
1593
1753
  // get the path to the currently active sdk
1594
1754
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
1595
1755
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
1756
+ const rezDecl = `module Rez { ${[
1757
+ "Drawables",
1758
+ "Fonts",
1759
+ "JsonData",
1760
+ "Layouts",
1761
+ "Menus",
1762
+ "Strings",
1763
+ ]
1764
+ .map((s) => ` module ${s} {}\n`)
1765
+ .join("")}}`;
1596
1766
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
1597
1767
  .toString()
1598
1768
  .replace(/\r\n/g, "\n")
1599
1769
  .replace(/^\s*\[.*?\]\s*$/gm, "")
1600
1770
  //.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
1601
- .replace(/^(\s*type)\s/gm, "$1def ");
1771
+ .replace(/^(\s*type)\s/gm, "$1def ") +
1772
+ (barrelList || [])
1773
+ .map((name) => `module ${name} { ${rezDecl} }`)
1774
+ .concat(rezDecl)
1775
+ .join("");
1602
1776
  try {
1603
1777
  const result = api_collectNamespaces(parser.parse(api, null, {
1604
1778
  filepath: "api.mir",
@@ -1616,19 +1790,19 @@ async function api_getApiMapping(state) {
1616
1790
  (value.type !== "VariableDeclarator" || value.kind != "const")) {
1617
1791
  throw `Negative constant ${fixup} did not refer to a constant`;
1618
1792
  }
1619
- const init = value.init;
1793
+ const init = getLiteralNode(value.init);
1620
1794
  if (!init || init.type !== "Literal") {
1621
1795
  throw `Negative constant ${fixup} was not a Literal`;
1622
1796
  }
1623
1797
  if (typeof init.value !== "number") {
1624
- console.log(`Negative fixup ${fixup} was already not a number!`);
1798
+ console.log(`Negative fixup ${fixup} was not a number!`);
1625
1799
  }
1626
1800
  else if (init.value > 0) {
1627
1801
  init.value = -init.value;
1628
1802
  init.raw = "-" + init.raw;
1629
1803
  }
1630
1804
  else {
1631
- console.log(`Negative fixup ${fixup} was already negative!`);
1805
+ // console.log(`Negative fixup ${fixup} was already negative!`);
1632
1806
  }
1633
1807
  });
1634
1808
  return result;
@@ -1647,67 +1821,195 @@ function api_isStateNode(node) {
1647
1821
  function api_variableDeclarationName(node) {
1648
1822
  return ("left" in node ? node.left : node).name;
1649
1823
  }
1650
- function checkOne(ns, decls, name) {
1824
+ function lookupToStateNodeDecls(results) {
1825
+ return results.reduce((result, current) => current.results.length
1826
+ ? result
1827
+ ? result.concat(current.results)
1828
+ : current.results
1829
+ : result, null);
1830
+ }
1831
+ function checkOne(state, ns, decls, node, isStatic) {
1832
+ // follow the superchain, looking up node in each class
1833
+ const superChain = (cls) => {
1834
+ if (!cls.superClass || cls.superClass === true) {
1835
+ return null;
1836
+ }
1837
+ return cls.superClass.reduce((result, sup) => {
1838
+ const next = api_hasProperty(sup[decls], node.name)
1839
+ ? sup[decls][node.name]
1840
+ : superChain(sup);
1841
+ return next ? (result ? result.concat(next) : next) : result;
1842
+ }, null);
1843
+ };
1844
+ const lookupInContext = (ns) => {
1845
+ const [, lkup] = lookup(state, decls, node, null, ns.stack);
1846
+ return lkup && lookupToStateNodeDecls(lkup);
1847
+ };
1848
+ // follow the superchain, looking up node in each class's scope
1849
+ const superChainScopes = (ns) => {
1850
+ const result = lookupInContext(ns);
1851
+ if (result)
1852
+ return result;
1853
+ if (!ns.superClass || ns.superClass === true) {
1854
+ return null;
1855
+ }
1856
+ return ns.superClass.reduce((result, sup) => {
1857
+ const next = superChainScopes(sup);
1858
+ return next ? (result ? result.concat(next) : next) : result;
1859
+ }, null);
1860
+ };
1651
1861
  if (api_isStateNode(ns)) {
1652
- if (api_hasProperty(ns[decls], name)) {
1653
- return ns[decls][name];
1654
- }
1655
- if (ns.type == "ClassDeclaration" &&
1656
- ns.superClass &&
1657
- ns.superClass !== true) {
1658
- const found = ns.superClass
1659
- .map((cls) => checkOne(cls, decls, name))
1660
- .filter((n) => n != null)
1661
- .flat(1);
1662
- return found.length ? found : null;
1862
+ if (api_hasProperty(ns[decls], node.name)) {
1863
+ return ns[decls][node.name];
1864
+ }
1865
+ switch (ns.type) {
1866
+ case "ClassDeclaration":
1867
+ if (!isStatic) {
1868
+ return superChain(ns) || superChainScopes(ns) || false;
1869
+ }
1870
+ // fall through
1871
+ case "ModuleDeclaration":
1872
+ return lookupInContext(ns) || false;
1663
1873
  }
1664
1874
  }
1665
1875
  return null;
1666
1876
  }
1667
- function lookup(state, decls, node, name, stack) {
1668
- stack || (stack = state.stack);
1877
+ function sameStateNodeDecl(a, b) {
1878
+ if (a === b)
1879
+ return true;
1880
+ if (!a || !b)
1881
+ return false;
1882
+ if (!api_isStateNode(a) || a.type !== b.type)
1883
+ return false;
1884
+ return (a.node === b.node ||
1885
+ a.type === "Program" ||
1886
+ (a.type === "ModuleDeclaration" && a.fullName === b.fullName));
1887
+ }
1888
+ function sameLookupDefinition(a, b) {
1889
+ return (
1890
+ // sameStateNodeDecl(a.parent, b.parent) &&
1891
+ (0,external_util_cjs_namespaceObject.sameArrays)(a.results, b.results, (ar, br) => sameStateNodeDecl(ar, br)));
1892
+ }
1893
+ function api_sameLookupResult(a, b) {
1894
+ return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
1895
+ }
1896
+ /**
1897
+ *
1898
+ * @param state - The ProgramState
1899
+ * @param decls - The field to use to look things up. either "decls" or "type_decls"
1900
+ * @param node - The node to lookup
1901
+ * @param name - Overrides the name of the node.
1902
+ * @param stack - if provided, use this stack, rather than the current
1903
+ * state.stack for the lookup
1904
+ * @param nonlocal - when true, a plain identifier will be looked up as a
1905
+ * non-local. This is needed when looking up a callee.
1906
+ * If the callee is a MemberExpression, the flag is ignored.
1907
+ * @returns
1908
+ * - [string, LookupDefinition[]] - if the lookup succeeds
1909
+ * - [false, false] - if the lookup fails, but its not an error because its not the kind of expression we can lookup
1910
+ * - [null, null] - if the lookup fails unexpectedly.
1911
+ */
1912
+ function lookup(state, decls, node, name, maybeStack, nonlocal) {
1913
+ const stack = maybeStack || state.stack;
1669
1914
  switch (node.type) {
1670
1915
  case "MemberExpression": {
1671
1916
  if (node.property.type != "Identifier" || node.computed)
1672
1917
  break;
1673
- const [, module, where] = lookup(state, decls, node.object, name, stack);
1674
- if (module && module.length === 1) {
1675
- const result = checkOne(module[0], decls, node.property.name);
1676
- if (result) {
1677
- return [
1678
- name || node.property.name,
1679
- result,
1680
- where.concat(module[0]),
1681
- ];
1918
+ const property = node.property;
1919
+ let result;
1920
+ if (node.object.type === "ThisExpression") {
1921
+ [, result] = lookup(state, decls, node.property, name, stack, true);
1922
+ }
1923
+ else {
1924
+ const [, results] = lookup(state, decls, node.object, name, stack, false);
1925
+ if (results === false)
1926
+ break;
1927
+ if (!results)
1928
+ return [null, null];
1929
+ result = results.reduce((current, lookupDef) => {
1930
+ const items = lookupDef.results
1931
+ .map((module) => {
1932
+ const res = checkOne(state, module, decls, property, false);
1933
+ return res ? { parent: module, results: res } : null;
1934
+ })
1935
+ .filter((r) => r != null);
1936
+ if (!items.length)
1937
+ return current;
1938
+ return current ? current.concat(items) : items;
1939
+ }, null);
1940
+ if (!result &&
1941
+ results.some((ld) => ld.results.some((sn) => (api_isStateNode(sn) && sn.fullName?.match(/^\$\.(\w+\.)?Rez\./)) ||
1942
+ sn.type === "VariableDeclarator" ||
1943
+ sn.type === "Identifier" ||
1944
+ sn.type === "BinaryExpression" ||
1945
+ (sn.type === "ClassDeclaration" &&
1946
+ property.name === "initialize")))) {
1947
+ // - The Rez module can contain lots of things from the resource
1948
+ // compiler which we don't track.
1949
+ // - Variables, and formal parameters would require type tracking
1950
+ // which we don't yet do
1951
+ // - Its ok to call an undeclared initialize method.
1952
+ // Report them all as "expected failures".
1953
+ return [false, false];
1682
1954
  }
1683
1955
  }
1684
- break;
1956
+ if (!result)
1957
+ return [null, null];
1958
+ return [name || property.name, result];
1685
1959
  }
1686
1960
  case "ThisExpression": {
1687
- for (let i = stack.length; i--;) {
1961
+ for (let i = stack.length;;) {
1688
1962
  const si = stack[i];
1689
1963
  if (si.type == "ModuleDeclaration" ||
1690
1964
  si.type == "ClassDeclaration" ||
1691
1965
  !i) {
1692
- return [name || si.name, [si], stack.slice(0, i)];
1966
+ return [
1967
+ name || si.name,
1968
+ [{ parent: i ? stack[i - 1] : null, results: [si] }],
1969
+ ];
1693
1970
  }
1694
1971
  }
1695
- break;
1696
1972
  }
1697
1973
  case "Identifier": {
1698
1974
  if (node.name == "$") {
1699
- return [name || node.name, [stack[0]], []];
1975
+ return [name || node.name, [{ parent: null, results: [stack[0]] }]];
1700
1976
  }
1977
+ let inStatic = false;
1978
+ let checkedImports = false;
1701
1979
  for (let i = stack.length; i--;) {
1702
- const result = checkOne(stack[i], decls, node.name);
1703
- if (result) {
1704
- return [name || node.name, result, stack.slice(0, i + 1)];
1980
+ const si = stack[i];
1981
+ switch (si.type) {
1982
+ case "ClassDeclaration":
1983
+ case "ModuleDeclaration":
1984
+ case "Program":
1985
+ if (!checkedImports) {
1986
+ checkedImports = true;
1987
+ const results = findUsingForNode(state, stack, i, node, decls === "type_decls");
1988
+ if (results) {
1989
+ return [name || node.name, [{ parent: si, results }]];
1990
+ }
1991
+ }
1992
+ break;
1993
+ case "FunctionDeclaration":
1994
+ inStatic = si.isStatic === true;
1995
+ // fall through
1996
+ default:
1997
+ if (nonlocal)
1998
+ continue;
1999
+ break;
2000
+ }
2001
+ const results = checkOne(state, si, decls, node, inStatic);
2002
+ if (results) {
2003
+ return [name || node.name, [{ parent: si, results }]];
2004
+ }
2005
+ else if (results === false) {
2006
+ break;
1705
2007
  }
1706
2008
  }
1707
- break;
2009
+ return [null, null];
1708
2010
  }
1709
2011
  }
1710
- return [null, null, null];
2012
+ return [false, false];
1711
2013
  }
1712
2014
  function api_collectNamespaces(ast, stateIn) {
1713
2015
  const state = (stateIn || {});
@@ -1739,8 +2041,12 @@ function api_collectNamespaces(ast, stateIn) {
1739
2041
  }
1740
2042
  };
1741
2043
  state.lookup = (node, name, stack) => lookup(state, state.inType ? "type_decls" : "decls", node, name, stack);
2044
+ state.lookupNonlocal = (node, name, stack) => lookup(state, "decls", node, name, stack, true);
1742
2045
  state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
1743
2046
  state.lookupType = (node, name, stack) => lookup(state, "type_decls", node, name, stack);
2047
+ state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
2048
+ ? { ...elm }
2049
+ : elm);
1744
2050
  state.inType = false;
1745
2051
  state.traverse = (root) => api_traverseAst(root, (node) => {
1746
2052
  try {
@@ -1753,14 +2059,60 @@ function api_collectNamespaces(ast, stateIn) {
1753
2059
  if (state.stack.length != 1) {
1754
2060
  throw new Error("Unexpected stack length for Program node");
1755
2061
  }
2062
+ state.stack[0].node = node;
1756
2063
  break;
1757
2064
  case "TypeSpecList":
1758
2065
  state.inType = true;
1759
2066
  break;
2067
+ case "ImportModule":
2068
+ case "Using": {
2069
+ const [parent] = state.stack.slice(-1);
2070
+ if (!parent.usings) {
2071
+ parent.usings = {};
2072
+ }
2073
+ const name = (node.type === "Using" && node.as && node.as.name) ||
2074
+ (node.id.type === "Identifier"
2075
+ ? node.id.name
2076
+ : node.id.property.name);
2077
+ const using = { node };
2078
+ parent.usings[name] = using;
2079
+ if (node.type == "ImportModule") {
2080
+ if (!parent.imports) {
2081
+ parent.imports = [using];
2082
+ }
2083
+ else {
2084
+ const index = parent.imports.findIndex((using) => (using.node.id.type === "Identifier"
2085
+ ? using.node.id.name
2086
+ : using.node.id.property.name) === name);
2087
+ if (index >= 0)
2088
+ parent.imports.splice(index, 1);
2089
+ parent.imports.push(using);
2090
+ }
2091
+ }
2092
+ break;
2093
+ }
2094
+ case "CatchClause":
2095
+ if (node.param) {
2096
+ const [parent] = state.stack.slice(-1);
2097
+ if (!parent.decls)
2098
+ parent.decls = {};
2099
+ const id = node.param.type === "Identifier"
2100
+ ? node.param
2101
+ : node.param.left;
2102
+ state.stack.push({
2103
+ type: "BlockStatement",
2104
+ fullName: undefined,
2105
+ name: undefined,
2106
+ node: node.body,
2107
+ decls: { [id.name]: [id] },
2108
+ });
2109
+ }
2110
+ break;
1760
2111
  case "BlockStatement": {
1761
2112
  const [parent] = state.stack.slice(-1);
1762
- if (parent.type != "FunctionDeclaration" &&
1763
- parent.type != "BlockStatement") {
2113
+ if (parent.node === node ||
2114
+ (parent.type != "FunctionDeclaration" &&
2115
+ parent.type != "BlockStatement")) {
1764
2116
  break;
1765
2117
  }
1766
2118
  // fall through
@@ -1806,6 +2158,9 @@ function api_collectNamespaces(ast, stateIn) {
1806
2158
  parent.decls[name].push(elm);
1807
2159
  if (node.type == "ModuleDeclaration" ||
1808
2160
  node.type == "ClassDeclaration") {
2161
+ // Inject the class/module name into itself,
2162
+ // so you can say Graphics.Graphics.Graphics.COLOR_RED
2163
+ elm.decls = { [name]: [elm] };
1809
2164
  if (!parent.type_decls)
1810
2165
  parent.type_decls = {};
1811
2166
  if (!api_hasProperty(parent.type_decls, name)) {
@@ -1853,7 +2208,7 @@ function api_collectNamespaces(ast, stateIn) {
1853
2208
  if (!parent.decls)
1854
2209
  parent.decls = {};
1855
2210
  const decls = parent.decls;
1856
- const stack = state.stack.slice();
2211
+ const stack = state.stackClone();
1857
2212
  node.declarations.forEach((decl) => {
1858
2213
  const name = api_variableDeclarationName(decl.id);
1859
2214
  if (!api_hasProperty(decls, name)) {
@@ -1957,8 +2312,14 @@ function api_collectNamespaces(ast, stateIn) {
1957
2312
  state.inType = true;
1958
2313
  break;
1959
2314
  }
1960
- if (state.stack.slice(-1).pop()?.node === node) {
1961
- state.stack.pop();
2315
+ const [parent] = state.stack.slice(-1);
2316
+ if (parent.node === node ||
2317
+ (node.type === "CatchClause" && parent.node === node.body)) {
2318
+ delete parent.usings;
2319
+ delete parent.imports;
2320
+ if (node.type != "Program") {
2321
+ state.stack.pop();
2322
+ }
1962
2323
  }
1963
2324
  }
1964
2325
  if (ret === false) {
@@ -2040,16 +2401,7 @@ function api_traverseAst(node, pre, post) {
2040
2401
  }
2041
2402
  return post && post(node);
2042
2403
  }
2043
- function formatAst(node, monkeyCSource = null) {
2044
- if ("comments" in node && !monkeyCSource) {
2045
- // Prettier inserts comments by using the source location to
2046
- // find the original comment, rather than using the contents
2047
- // of the comment as reported by the comment nodes themselves.
2048
- // If all we've got is the ast, rather than the actual
2049
- // source code, this goes horribly wrong, so just drop all
2050
- // the comments.
2051
- delete node.comments;
2052
- }
2404
+ function api_formatAst(node, monkeyCSource = null) {
2053
2405
  /*
2054
2406
  * The estree printer sometimes looks at the parent node without
2055
2407
  * checking that there *is* a parent node (eg it assumes all
@@ -2090,7 +2442,7 @@ function handleException(state, node, exception) {
2090
2442
  .filter((e) => e != null)
2091
2443
  .join(".");
2092
2444
  const location = node.loc && node.loc.source
2093
- ? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
2445
+ ? `${node.loc.source}:${node.loc.start.line || 0}:${node.loc.end.line || 0}`
2094
2446
  : "<unknown>";
2095
2447
  const message = `Got exception \`${exception instanceof Error
2096
2448
  ? exception.message
@@ -2106,6 +2458,61 @@ function handleException(state, node, exception) {
2106
2458
  throw exception;
2107
2459
  }
2108
2460
  }
2461
+ function findUsing(state, stack, using) {
2462
+ if (using.module)
2463
+ return using.module;
2464
+ let module = stack[0];
2465
+ const find = (node) => {
2466
+ let name;
2467
+ if (node.type == "Identifier") {
2468
+ name = node.name;
2469
+ }
2470
+ else {
2471
+ find(node.object);
2472
+ name = node.property.name;
2473
+ }
2474
+ if (api_hasProperty(module.decls, name)) {
2475
+ const decls = module.decls[name];
2476
+ if (decls &&
2477
+ decls.length === 1 &&
2478
+ decls[0].type === "ModuleDeclaration") {
2479
+ module = decls[0];
2480
+ return true;
2481
+ }
2482
+ }
2483
+ return false;
2484
+ };
2485
+ if (find(using.node.id)) {
2486
+ using.module = module;
2487
+ return using.module;
2488
+ }
2489
+ if (state.config?.checkInvalidSymbols !== "OFF") {
2490
+ inliner_diagnostic(state, using.node.id.loc, `Unable to resolve import of ${api_formatAst(using.node.id)}`, state.config?.checkInvalidSymbols || "WARNING");
2491
+ }
2492
+ return null;
2493
+ }
2494
+ function findUsingForNode(state, stack, i, node, isType) {
2495
+ while (i >= 0) {
2496
+ const si = stack[i--];
2497
+ if (api_hasProperty(si.usings, node.name)) {
2498
+ const using = si.usings[node.name];
2499
+ const module = findUsing(state, stack, using);
2500
+ return module && [module];
2501
+ }
2502
+ if (si.imports && isType) {
2503
+ for (let j = si.imports.length; j--;) {
2504
+ const using = si.imports[j];
2505
+ const module = findUsing(state, stack, using);
2506
+ if (using.module) {
2507
+ if (api_hasProperty(using.module.type_decls, node.name)) {
2508
+ return using.module.type_decls[node.name];
2509
+ }
2510
+ }
2511
+ }
2512
+ }
2513
+ }
2514
+ return null;
2515
+ }
2109
2516
 
2110
2517
  var __webpack_export_target__ = exports;
2111
2518
  for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];