@markw65/monkeyc-optimizer 1.0.21 → 1.0.24

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),
60
- "traverseAst": () => (/* binding */ api_traverseAst),
61
- "variableDeclarationName": () => (/* binding */ api_variableDeclarationName)
61
+ "sameLookupResult": () => (/* binding */ api_sameLookupResult),
62
+ "traverseAst": () => (/* reexport */ ast_traverseAst),
63
+ "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
64
+ "visitReferences": () => (/* reexport */ visitor_visitReferences)
62
65
  });
63
66
 
64
67
  ;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
@@ -68,10 +71,148 @@ var prettier_plugin_monkeyc_default = /*#__PURE__*/__webpack_require__.n(prettie
68
71
  const promises_namespaceObject = require("fs/promises");
69
72
  ;// CONCATENATED MODULE: external "prettier"
70
73
  const external_prettier_namespaceObject = require("prettier");
74
+ ;// CONCATENATED MODULE: ./src/ast.ts
75
+ /*
76
+ * This ensures that mctreeTypeInfo has every key of MCTreeTypeInfo,
77
+ * and that the corresponding arrays contain every element of the
78
+ * corresponding type.
79
+ *
80
+ * ie, any time mctree.Node changes, we'll get errors here if
81
+ * mctreeTypeInfo needs updating.
82
+ *
83
+ */
84
+ function _check(x) {
85
+ const y = x;
86
+ x = y;
87
+ }
88
+ const mctreeTypeInfo = {
89
+ ArrayExpression: ["elements"],
90
+ AssignmentExpression: ["left", "right"],
91
+ AttributeList: ["attributes"],
92
+ Attributes: ["elements"],
93
+ BinaryExpression: ["left", "right"],
94
+ Block: [],
95
+ BlockStatement: ["body", "innerComments"],
96
+ BreakStatement: [],
97
+ CallExpression: ["callee", "arguments"],
98
+ CatchClause: ["param", "body"],
99
+ CatchClauses: ["catches"],
100
+ ClassBody: ["body"],
101
+ ClassDeclaration: ["attrs", "id", "superClass", "body"],
102
+ ClassElement: ["item"],
103
+ ConditionalExpression: ["test", "consequent", "alternate"],
104
+ ContinueStatement: [],
105
+ DoWhileStatement: ["body", "test"],
106
+ EnumDeclaration: ["attrs", "id", "body"],
107
+ EnumStringBody: ["members"],
108
+ EnumStringMember: ["id", "init"],
109
+ ExpressionStatement: ["expression"],
110
+ ForStatement: ["init", "test", "body", "update"],
111
+ FunctionDeclaration: ["attrs", "id", "params", "body"],
112
+ Identifier: [],
113
+ IfStatement: ["test", "consequent", "alternate"],
114
+ ImportModule: ["id"],
115
+ InstanceOfCase: ["id"],
116
+ Line: [],
117
+ Literal: [],
118
+ LogicalExpression: ["left", "right"],
119
+ MemberExpression: ["object", "property"],
120
+ MethodDefinition: ["params", "returnType"],
121
+ ModuleDeclaration: ["attrs", "id", "body"],
122
+ MultiLine: [],
123
+ NewExpression: ["callee", "arguments"],
124
+ ObjectExpression: ["properties"],
125
+ ParenthesizedExpression: ["expression"],
126
+ Program: ["body", "comments"],
127
+ Property: ["key", "value"],
128
+ ReturnStatement: ["argument"],
129
+ SequenceExpression: ["expressions"],
130
+ SizedArrayExpression: ["size", "ts"],
131
+ SwitchCase: ["test", "consequent"],
132
+ SwitchStatement: ["discriminant", "cases"],
133
+ ThisExpression: [],
134
+ ThrowStatement: ["argument"],
135
+ TryStatement: ["block", "handler", "finalizer"],
136
+ TypedefDeclaration: ["attrs", "id", "ts"],
137
+ TypeSpecList: ["ts"],
138
+ TypeSpecPart: ["name", "body", "callspec", "generics"],
139
+ UnaryExpression: ["argument"],
140
+ UpdateExpression: ["argument"],
141
+ Using: ["id", "as"],
142
+ VariableDeclaration: ["attrs", "declarations"],
143
+ VariableDeclarator: ["id", "init"],
144
+ WhileStatement: ["test", "body"],
145
+ };
146
+ function isMCTreeNode(node) {
147
+ return node ? typeof node === "object" && "type" in node : false;
148
+ }
149
+ /*
150
+ * Traverse the ast rooted at node, calling pre before
151
+ * visiting each node, and post after.
152
+ *
153
+ * - if pre returns false, the node is not traversed, and
154
+ * post is not called;
155
+ * - if pre returns a list of child nodes, only those will
156
+ * be traversed
157
+ * - otherwise all child nodes are traversed
158
+ *
159
+ * - if post returns false, the node it was called on is
160
+ * removed.
161
+ */
162
+ function ast_traverseAst(node, pre, post) {
163
+ const nodes = pre && pre(node);
164
+ if (nodes === false)
165
+ return;
166
+ if (!mctreeTypeInfo[node.type]) {
167
+ throw new Error("what?");
168
+ }
169
+ for (const key of nodes || mctreeTypeInfo[node.type]) {
170
+ const value = node[key];
171
+ if (!value)
172
+ continue;
173
+ if (Array.isArray(value)) {
174
+ const values = value;
175
+ const deletions = values.reduce((state, obj, i) => {
176
+ if (isMCTreeNode(obj)) {
177
+ const repl = ast_traverseAst(obj, pre, post);
178
+ if (repl === false) {
179
+ if (!state)
180
+ state = {};
181
+ state[i] = true;
182
+ }
183
+ else if (repl != null) {
184
+ if (!state)
185
+ state = {};
186
+ values[i] = repl;
187
+ }
188
+ }
189
+ return state;
190
+ }, null);
191
+ if (deletions) {
192
+ values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
193
+ }
194
+ }
195
+ else if (isMCTreeNode(value)) {
196
+ const repl = ast_traverseAst(value, pre, post);
197
+ if (repl === false) {
198
+ delete node[key];
199
+ }
200
+ else if (repl != null) {
201
+ if (Array.isArray(repl)) {
202
+ throw new Error("Array returned by traverseAst in Node context");
203
+ }
204
+ node[key] = repl;
205
+ }
206
+ }
207
+ }
208
+ return post && post(node);
209
+ }
210
+
71
211
  ;// CONCATENATED MODULE: external "./api.cjs"
72
212
  const external_api_cjs_namespaceObject = require("./api.cjs");
73
213
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
74
214
 
215
+
75
216
  function variable_renamer_renameVariable(state, locals, declName) {
76
217
  const map = locals.map;
77
218
  if (!hasProperty(map, declName))
@@ -128,6 +269,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
128
269
  ;// CONCATENATED MODULE: ./src/inliner.ts
129
270
 
130
271
 
272
+
131
273
  function getArgSafety(state, func, args, requireAll) {
132
274
  // determine whether decl might be changed by a function call
133
275
  // or assignment during the evaluation of FunctionStateNode.
@@ -169,11 +311,13 @@ function getArgSafety(state, func, args, requireAll) {
169
311
  case "Identifier":
170
312
  case "MemberExpression": {
171
313
  const [, results] = state.lookup(arg);
172
- if (!results || results.length !== 1) {
314
+ if (!results ||
315
+ results.length !== 1 ||
316
+ results[0].results.length !== 1) {
173
317
  safeArgs.push(null);
174
318
  return !requireAll;
175
319
  }
176
- const safety = getSafety(results[0]);
320
+ const safety = getSafety(results[0].results[0]);
177
321
  safeArgs.push(safety);
178
322
  if (!safety) {
179
323
  allSafe = false;
@@ -193,25 +337,12 @@ function getArgSafety(state, func, args, requireAll) {
193
337
  if (allSafe && requireAll)
194
338
  return true;
195
339
  let callSeen = false;
196
- let ok = true;
197
340
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
198
- const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
199
341
  // look for uses of "unsafe" args that occur after a call.
200
342
  // use post to do the checking, because arguments are evaluated
201
343
  // prior to the call, so eg "return f(x.y);" is fine, but
202
344
  // "return f()+x.y" is not.
203
- //
204
- // We also have to use a "pre" to ensure that child nodes are
205
- // visited in source order (otherwise we could visit x.y before f()
206
- // in the above example)
207
- traverseAst(func.node.body, (node) => {
208
- return Object.entries(node)
209
- .filter((kv) => Array.isArray(kv[1])
210
- ? kv[1].length !== 0 && hasProperty(kv[1][0], "type")
211
- : hasProperty(kv[1], "type"))
212
- .sort(([, a], [, b]) => getLoc(a) - getLoc(b))
213
- .map(([key]) => key);
214
- }, (node) => {
345
+ traverseAst(func.node.body, null, (node) => {
215
346
  switch (node.type) {
216
347
  case "AssignmentExpression":
217
348
  case "UpdateExpression": {
@@ -265,12 +396,6 @@ function inliningLooksUseful(func, node) {
265
396
  }
266
397
  return false;
267
398
  }
268
- var InlineStatus;
269
- (function (InlineStatus) {
270
- InlineStatus[InlineStatus["Never"] = 0] = "Never";
271
- InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
272
- InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
273
- })(InlineStatus || (InlineStatus = {}));
274
399
  function inlineRequested(state, func) {
275
400
  const excludeAnnotations = (func.node.loc?.source &&
276
401
  state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
@@ -280,7 +405,7 @@ function inlineRequested(state, func) {
280
405
  func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
281
406
  (attr.argument.name === "inline" ||
282
407
  (attr.argument.name.startsWith("inline_") &&
283
- hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
408
+ !hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
284
409
  return true;
285
410
  }
286
411
  return false;
@@ -450,13 +575,13 @@ function inliner_unused(expression, top) {
450
575
  },
451
576
  ];
452
577
  }
453
- function diagnostic(state, loc, message) {
578
+ function inliner_diagnostic(state, loc, message, type = "INFO") {
454
579
  if (!loc || !loc.source)
455
580
  return;
456
581
  const source = loc.source;
457
582
  if (!state.diagnostics)
458
583
  state.diagnostics = {};
459
- if (!hasProperty(state.diagnostics, source)) {
584
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
460
585
  if (!message)
461
586
  return;
462
587
  state.diagnostics[source] = [];
@@ -466,7 +591,7 @@ function diagnostic(state, loc, message) {
466
591
  if (message) {
467
592
  if (index < 0)
468
593
  index = diags.length;
469
- diags[index] = { type: "INFO", loc, message };
594
+ diags[index] = { type, loc, message };
470
595
  }
471
596
  else if (index >= 0) {
472
597
  diags.splice(index, 1);
@@ -474,7 +599,7 @@ function diagnostic(state, loc, message) {
474
599
  }
475
600
  function inlineDiagnostic(state, func, call, message) {
476
601
  if (inlineRequested(state, func)) {
477
- diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
602
+ inliner_diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
478
603
  }
479
604
  }
480
605
  function inlineWithArgs(state, func, call, context) {
@@ -522,7 +647,7 @@ function inlineWithArgs(state, func, call, context) {
522
647
  if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
523
648
  return null;
524
649
  }
525
- diagnostic(state, call.loc, null);
650
+ inliner_diagnostic(state, call.loc, null);
526
651
  if (context.type !== "ReturnStatement" && retStmtCount) {
527
652
  const last = body.body[body.body.length - 1];
528
653
  if (last.type != "ReturnStatement") {
@@ -585,23 +710,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
585
710
  // With a bit more work, we could find the guaranteed shortest
586
711
  // reference, and then use this to optimize *all* symbols, not
587
712
  // just fix inlined ones.
588
- if (current &&
589
- current.length === original.length &&
590
- current.every((item, index) => item == original[index])) {
713
+ if (current && sameLookupResult(original, current)) {
591
714
  return lookupNode;
592
715
  }
593
716
  const node = lookupNode.type === "Identifier"
594
717
  ? lookupNode
595
718
  : lookupNode.property;
596
- if (original.length === 1 && original[0].type === "EnumStringMember") {
597
- return applyTypeIfNeeded(original[0].init);
719
+ if (original.length === 1 &&
720
+ original[0].results.length === 1 &&
721
+ original[0].results[0].type === "EnumStringMember") {
722
+ return applyTypeIfNeeded(original[0].results[0].init);
598
723
  }
599
- const prefixes = original.map((sn) => {
724
+ const prefixes = original
725
+ .map((lookupDef) => lookupDef.results.map((sn) => {
600
726
  if (isStateNode(sn) && sn.fullName) {
601
727
  return sn.fullName;
602
728
  }
603
729
  return "";
604
- });
730
+ }))
731
+ .flat();
605
732
  if (prefixes.length &&
606
733
  prefixes[0].startsWith("$.") &&
607
734
  prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
@@ -611,9 +738,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
611
738
  if (found)
612
739
  return current;
613
740
  const [, results] = state.lookup(current);
614
- if (results &&
615
- results.length === original.length &&
616
- results.every((result, i) => result === original[i])) {
741
+ if (results && sameLookupResult(original, results)) {
617
742
  found = true;
618
743
  return current;
619
744
  }
@@ -655,6 +780,99 @@ function fixNodeScope(state, lookupNode, nodeStack) {
655
780
 
656
781
  ;// CONCATENATED MODULE: external "./util.cjs"
657
782
  const external_util_cjs_namespaceObject = require("./util.cjs");
783
+ ;// CONCATENATED MODULE: ./src/visitor.ts
784
+
785
+ function visitor_visitReferences(state, ast, name, defn, callback) {
786
+ const checkResults = ([name, results], node) => {
787
+ if (name && results) {
788
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
789
+ if (callback(node, results, false) === false) {
790
+ return [];
791
+ }
792
+ }
793
+ }
794
+ else if (defn === false) {
795
+ if (callback(node, [], results === null) === false) {
796
+ return [];
797
+ }
798
+ }
799
+ return null;
800
+ };
801
+ state.pre = (node) => {
802
+ switch (node.type) {
803
+ case "AttributeList":
804
+ return [];
805
+ case "UnaryExpression":
806
+ // a bare symbol isn't a reference
807
+ if (node.operator === ":")
808
+ return [];
809
+ break;
810
+ case "BinaryExpression":
811
+ /*
812
+ * `expr has :symbol` can be treated as a reference
813
+ * to expr.symbol.
814
+ */
815
+ if (node.operator === "has") {
816
+ if (node.right.type === "UnaryExpression" &&
817
+ node.right.operator === ":") {
818
+ if (!name || node.right.argument.name === name) {
819
+ return checkResults(state.lookup({
820
+ type: "MemberExpression",
821
+ object: node.left,
822
+ property: node.right.argument,
823
+ computed: false,
824
+ }), node.right.argument);
825
+ }
826
+ }
827
+ }
828
+ break;
829
+ case "CallExpression":
830
+ // A call expression whose callee is an identifier is looked
831
+ // up as a non-local. ie even if there's a same named local,
832
+ // it will be ignored, and the lookup will start as if the
833
+ // call had been written self.foo() rather than foo().
834
+ if (node.callee.type === "Identifier") {
835
+ if (!name || node.callee.name === name) {
836
+ /* ignore return value */
837
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
838
+ }
839
+ return ["arguments"];
840
+ }
841
+ break;
842
+ case "Identifier":
843
+ if (!name || node.name === name) {
844
+ return checkResults(state.lookup(node), node);
845
+ }
846
+ break;
847
+ case "MemberExpression":
848
+ if (!node.computed && node.property.type === "Identifier") {
849
+ if (!name || node.property.name === name) {
850
+ return checkResults(state.lookup(node), node) || ["object"];
851
+ }
852
+ return ["object"];
853
+ }
854
+ break;
855
+ case "MethodDefinition": {
856
+ if (!state.inType) {
857
+ throw new Error("Method definition outside of type!");
858
+ }
859
+ if (node.params) {
860
+ node.params.forEach((param) => {
861
+ if (param.type == "BinaryExpression") {
862
+ state.traverse(param.right);
863
+ state.inType = true;
864
+ }
865
+ });
866
+ }
867
+ return ["returnType"];
868
+ }
869
+ }
870
+ return null;
871
+ };
872
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
873
+ delete state.pre;
874
+ }
875
+
658
876
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
659
877
 
660
878
 
@@ -662,43 +880,25 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
662
880
 
663
881
 
664
882
 
665
- function processImports(allImports, lookup) {
666
- allImports.forEach(({ node, stack }) => {
667
- const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
668
- if (name && module) {
669
- const [parent] = stack.slice(-1);
670
- if (!parent.decls)
671
- parent.decls = {};
672
- const decls = parent.decls;
673
- if (!hasProperty(decls, name))
674
- decls[name] = [];
675
- module.forEach((m) => {
676
- if (isStateNode(m) && m.type == "ModuleDeclaration") {
677
- pushUnique(decls[name], m);
678
- if (!parent.type_decls)
679
- parent.type_decls = {};
680
- const tdecls = parent.type_decls;
681
- if (!hasProperty(tdecls, name))
682
- tdecls[name] = [];
683
- pushUnique(tdecls[name], m);
684
- if (node.type == "ImportModule" && m.type_decls) {
685
- Object.entries(m.type_decls).forEach(([name, decls]) => {
686
- if (!hasProperty(tdecls, name))
687
- tdecls[name] = [];
688
- decls.forEach((decl) => pushUnique(tdecls[name], decl));
689
- });
690
- }
691
- }
692
- });
693
- }
694
- });
695
- }
883
+
884
+
696
885
  function collectClassInfo(state) {
886
+ const toybox = state.stack[0].decls["Toybox"][0];
887
+ const lang = toybox.decls["Lang"][0];
888
+ const object = lang.decls["Object"];
697
889
  state.allClasses.forEach((elm) => {
890
+ if (elm.stack[elm.stack.length - 1].type === "ClassDeclaration") {
891
+ // nested classes don't get access to their contained
892
+ // context. Put them in the global scope instead.
893
+ elm.stack = elm.stack.slice(0, 1);
894
+ }
698
895
  if (elm.node.superClass) {
699
- const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
700
- const superClass = classes &&
701
- classes.filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
896
+ const [name, lookupDefns] = state.lookup(elm.node.superClass, null, elm.stack);
897
+ const superClass = lookupDefns &&
898
+ lookupDefns
899
+ .map((lookupDefn) => lookupDefn.results)
900
+ .flat()
901
+ .filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
702
902
  // set it "true" if there is a superClass, but we can't find it.
703
903
  elm.superClass = superClass && superClass.length ? superClass : true;
704
904
  if (name && elm.superClass !== true) {
@@ -730,6 +930,9 @@ function collectClassInfo(state) {
730
930
  elm.decls[name] = elm.superClass;
731
931
  }
732
932
  }
933
+ else if (elm !== object[0]) {
934
+ elm.superClass = object;
935
+ }
733
936
  });
734
937
  const markOverrides = (cls, scls) => {
735
938
  if (scls === true)
@@ -760,7 +963,9 @@ function getFileSources(fnMap) {
760
963
  fs
761
964
  .readFile(name)
762
965
  .then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
763
- })).then(() => { });
966
+ })).then(() => {
967
+ return;
968
+ });
764
969
  }
765
970
  function getFileASTs(fnMap) {
766
971
  return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
@@ -783,11 +988,12 @@ function getFileASTs(fnMap) {
783
988
  return ok;
784
989
  }, true));
785
990
  }
786
- async function analyze(fnMap) {
991
+ async function analyze(fnMap, barrelList, config) {
787
992
  let hasTests = false;
788
- const allImports = [];
993
+ let markApi = true;
789
994
  const preState = {
790
995
  fnMap,
996
+ config,
791
997
  allFunctions: [],
792
998
  allClasses: [],
793
999
  shouldExclude(node) {
@@ -815,45 +1021,38 @@ async function analyze(fnMap) {
815
1021
  }
816
1022
  return false;
817
1023
  },
818
- post(node, state) {
1024
+ pre(node, state) {
819
1025
  switch (node.type) {
820
1026
  case "FunctionDeclaration":
1027
+ if (markApi) {
1028
+ node.body = null;
1029
+ break;
1030
+ }
1031
+ // falls through
1032
+ case "ModuleDeclaration":
821
1033
  case "ClassDeclaration": {
822
1034
  const [scope] = state.stack.slice(-1);
823
- const stack = state.stack.slice(0, -1);
824
- scope.stack = stack;
1035
+ scope.stack = state.stackClone().slice(0, -1);
825
1036
  if (scope.type == "FunctionDeclaration") {
1037
+ scope.isStatic =
1038
+ scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
1039
+ (scope.node.attrs &&
1040
+ scope.node.attrs.access &&
1041
+ scope.node.attrs.access.includes("static"));
826
1042
  state.allFunctions.push(scope);
827
1043
  }
828
- else {
1044
+ else if (scope.type === "ClassDeclaration") {
829
1045
  state.allClasses.push(scope);
830
1046
  }
831
- return null;
1047
+ break;
832
1048
  }
833
- case "Using":
834
- case "ImportModule":
835
- allImports.push({ node, stack: state.stack.slice() });
836
- return null;
837
- default:
838
- return null;
839
1049
  }
1050
+ return null;
840
1051
  },
841
1052
  };
842
- await getApiMapping(preState);
1053
+ await getApiMapping(preState, barrelList);
1054
+ markApi = false;
843
1055
  const state = preState;
844
- // Mark all functions from api.mir as "special" by
845
- // setting their bodies to null. In api.mir, they're
846
- // all empty, which makes it look like they're
847
- // do-nothing functions.
848
- const markApi = (node) => {
849
- if (node.type == "FunctionDeclaration") {
850
- node.node.body = null;
851
- }
852
- if (isStateNode(node) && node.decls) {
853
- Object.values(node.decls).forEach((v) => v.forEach(markApi));
854
- }
855
- };
856
- markApi(state.stack[0]);
857
1056
  await getFileASTs(fnMap);
858
1057
  Object.entries(fnMap).forEach(([name, value]) => {
859
1058
  const { ast, parserError } = value;
@@ -866,8 +1065,28 @@ async function analyze(fnMap) {
866
1065
  });
867
1066
  delete state.shouldExclude;
868
1067
  delete state.post;
869
- processImports(allImports, state.lookup);
870
1068
  collectClassInfo(state);
1069
+ const diagnosticType = config?.checkInvalidSymbols !== "OFF"
1070
+ ? config?.checkInvalidSymbols || "WARNING"
1071
+ : null;
1072
+ if (diagnosticType &&
1073
+ !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
1074
+ const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
1075
+ Object.entries(fnMap).forEach(([, v]) => {
1076
+ visitReferences(state, v.ast, null, false, (node, results, error) => {
1077
+ if (!error)
1078
+ return undefined;
1079
+ const nodeStr = formatAst(node);
1080
+ if (state.inType) {
1081
+ if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
1082
+ return undefined;
1083
+ }
1084
+ }
1085
+ diagnostic(state, node.loc, `Undefined symbol ${nodeStr}`, diagnosticType);
1086
+ return false;
1087
+ });
1088
+ });
1089
+ }
871
1090
  return state;
872
1091
  }
873
1092
  function compareLiteralLike(a, b) {
@@ -877,11 +1096,11 @@ function compareLiteralLike(a, b) {
877
1096
  b = b.left;
878
1097
  return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
879
1098
  }
880
- function getLiteralFromDecls(decls) {
881
- if (!decls.length)
1099
+ function getLiteralFromDecls(lookupDefns) {
1100
+ if (!lookupDefns.length)
882
1101
  return null;
883
1102
  let result = null;
884
- if (decls.every((d) => {
1103
+ if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
885
1104
  if (d.type === "EnumStringMember" ||
886
1105
  (d.type === "VariableDeclarator" && d.node.kind === "const")) {
887
1106
  const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
@@ -896,7 +1115,7 @@ function getLiteralFromDecls(decls) {
896
1115
  }
897
1116
  }
898
1117
  return false;
899
- })) {
1118
+ }))) {
900
1119
  return result;
901
1120
  }
902
1121
  return null;
@@ -1107,20 +1326,23 @@ function markFunctionCalled(state, func) {
1107
1326
  }
1108
1327
  pushUnique(state.calledFunctions[func.id.name], func);
1109
1328
  }
1110
- async function optimizeMonkeyC(fnMap) {
1329
+ async function optimizeMonkeyC(fnMap, barrelList, config) {
1111
1330
  const state = {
1112
- ...(await analyze(fnMap)),
1331
+ ...(await analyze(fnMap, barrelList, config)),
1113
1332
  localsStack: [{}],
1114
1333
  exposed: {},
1115
1334
  calledFunctions: {},
1116
1335
  };
1117
- const replace = (node) => {
1336
+ const replace = (node, old) => {
1118
1337
  if (node === false || node === null)
1119
1338
  return node;
1120
1339
  const rep = state.traverse(node);
1121
- return rep === false || rep ? rep : node;
1340
+ if (rep === false || Array.isArray(rep))
1341
+ return rep;
1342
+ return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
1122
1343
  };
1123
1344
  const inPlaceReplacement = (node, obj) => {
1345
+ const { start, end, loc } = node;
1124
1346
  for (const k of Object.keys(node)) {
1125
1347
  delete node[k];
1126
1348
  }
@@ -1128,13 +1350,16 @@ async function optimizeMonkeyC(fnMap) {
1128
1350
  obj = {
1129
1351
  type: "BinaryExpression",
1130
1352
  operator: "as",
1131
- left: obj,
1353
+ left: { ...obj, start, end, loc },
1132
1354
  right: { type: "TypeSpecList", ts: [obj.enumType] },
1133
1355
  };
1134
1356
  }
1135
1357
  for (const [k, v] of Object.entries(obj)) {
1136
1358
  node[k] = v;
1137
1359
  }
1360
+ node.loc = loc;
1361
+ node.start = start;
1362
+ node.end = end;
1138
1363
  };
1139
1364
  const lookupAndReplace = (node) => {
1140
1365
  const [, objects] = state.lookup(node);
@@ -1192,7 +1417,7 @@ async function optimizeMonkeyC(fnMap) {
1192
1417
  case "ConditionalExpression":
1193
1418
  case "IfStatement":
1194
1419
  case "DoWhileStatement":
1195
- case "WhileStatement":
1420
+ case "WhileStatement": {
1196
1421
  const test = (state.traverse(node.test) ||
1197
1422
  node.test);
1198
1423
  const [value, type] = getNodeValue(test);
@@ -1224,6 +1449,7 @@ async function optimizeMonkeyC(fnMap) {
1224
1449
  }
1225
1450
  }
1226
1451
  return null;
1452
+ }
1227
1453
  case "EnumDeclaration":
1228
1454
  return false;
1229
1455
  case "ForStatement": {
@@ -1253,6 +1479,37 @@ async function optimizeMonkeyC(fnMap) {
1253
1479
  }
1254
1480
  return ["init"];
1255
1481
  }
1482
+ case "CatchClause":
1483
+ if (node.param) {
1484
+ state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
1485
+ const locals = topLocals();
1486
+ const map = locals.map;
1487
+ const declName = variableDeclarationName(node.param);
1488
+ const name = renameVariable(state, locals, declName);
1489
+ if (name) {
1490
+ if (node.param.type === "Identifier") {
1491
+ node.param.name = name;
1492
+ }
1493
+ else {
1494
+ node.param.left.name = name;
1495
+ }
1496
+ }
1497
+ else {
1498
+ map[declName] = true;
1499
+ }
1500
+ return ["body"];
1501
+ }
1502
+ break;
1503
+ case "BinaryExpression":
1504
+ if (node.operator === "has") {
1505
+ if (node.right.type === "UnaryExpression" &&
1506
+ node.right.operator === ":") {
1507
+ // Using `expr has :symbol` doesn't "expose"
1508
+ // symbol. So skip the right operand.
1509
+ return ["left"];
1510
+ }
1511
+ }
1512
+ break;
1256
1513
  case "UnaryExpression":
1257
1514
  if (node.operator == ":") {
1258
1515
  // If we produce a Symbol, for a given name,
@@ -1335,7 +1592,7 @@ async function optimizeMonkeyC(fnMap) {
1335
1592
  }
1336
1593
  const opt = optimizeNode(node);
1337
1594
  if (opt) {
1338
- return replace(opt);
1595
+ return replace(opt, node);
1339
1596
  }
1340
1597
  switch (node.type) {
1341
1598
  case "ConditionalExpression":
@@ -1345,7 +1602,7 @@ async function optimizeMonkeyC(fnMap) {
1345
1602
  const rep = node.test.value ? node.consequent : node.alternate;
1346
1603
  if (!rep)
1347
1604
  return false;
1348
- return replace(rep);
1605
+ return replace(rep, rep);
1349
1606
  }
1350
1607
  break;
1351
1608
  case "WhileStatement":
@@ -1360,11 +1617,11 @@ async function optimizeMonkeyC(fnMap) {
1360
1617
  break;
1361
1618
  case "ReturnStatement":
1362
1619
  if (node.argument && node.argument.type === "CallExpression") {
1363
- return replace(optimizeCall(state, node.argument, node));
1620
+ return replace(optimizeCall(state, node.argument, node), node.argument);
1364
1621
  }
1365
1622
  break;
1366
1623
  case "CallExpression": {
1367
- return replace(optimizeCall(state, node, null));
1624
+ return replace(optimizeCall(state, node, null), node);
1368
1625
  }
1369
1626
  case "AssignmentExpression":
1370
1627
  if (node.operator === "=" &&
@@ -1376,7 +1633,7 @@ async function optimizeMonkeyC(fnMap) {
1376
1633
  break;
1377
1634
  case "ExpressionStatement":
1378
1635
  if (node.expression.type === "CallExpression") {
1379
- return replace(optimizeCall(state, node.expression, node));
1636
+ return replace(optimizeCall(state, node.expression, node), node.expression);
1380
1637
  }
1381
1638
  else if (node.expression.type === "AssignmentExpression") {
1382
1639
  if (node.expression.right.type === "CallExpression") {
@@ -1388,10 +1645,10 @@ async function optimizeMonkeyC(fnMap) {
1388
1645
  }
1389
1646
  if (!ok && node.expression.operator == "=") {
1390
1647
  const [, result] = state.lookup(node.expression.left);
1391
- ok = result != null;
1648
+ ok = !!result;
1392
1649
  }
1393
1650
  if (ok) {
1394
- return replace(optimizeCall(state, node.expression.right, node.expression));
1651
+ return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
1395
1652
  }
1396
1653
  }
1397
1654
  }
@@ -1399,7 +1656,7 @@ async function optimizeMonkeyC(fnMap) {
1399
1656
  const ret = unused(node.expression, true);
1400
1657
  if (ret) {
1401
1658
  return ret
1402
- .map(replace)
1659
+ .map((r) => replace(r, r))
1403
1660
  .flat(1)
1404
1661
  .filter((s) => !!s);
1405
1662
  }
@@ -1420,6 +1677,9 @@ async function optimizeMonkeyC(fnMap) {
1420
1677
  delete state.post;
1421
1678
  const cleanup = (node) => {
1422
1679
  switch (node.type) {
1680
+ case "ThisExpression":
1681
+ node.text = "self";
1682
+ break;
1423
1683
  case "EnumStringBody":
1424
1684
  if (node.members.every((m) => {
1425
1685
  const name = "name" in m ? m.name : m.id.name;
@@ -1484,6 +1744,19 @@ async function optimizeMonkeyC(fnMap) {
1484
1744
  return false;
1485
1745
  }
1486
1746
  break;
1747
+ case "ClassDeclaration":
1748
+ case "ModuleDeclaration":
1749
+ // none of the attributes means anything on classes and
1750
+ // modules, and the new compiler complains about some
1751
+ // of them. Just drop them all.
1752
+ if (node.attrs && node.attrs.access) {
1753
+ if (node.attrs.attributes) {
1754
+ delete node.attrs.access;
1755
+ }
1756
+ else {
1757
+ delete node.attrs;
1758
+ }
1759
+ }
1487
1760
  }
1488
1761
  return null;
1489
1762
  };
@@ -1499,9 +1772,12 @@ async function optimizeMonkeyC(fnMap) {
1499
1772
  return state.diagnostics;
1500
1773
  }
1501
1774
  function optimizeCall(state, node, context) {
1502
- const [name, results] = state.lookup(node.callee);
1775
+ const [name, results] = state.lookupNonlocal(node.callee);
1503
1776
  const callees = results &&
1504
- results.filter((c) => c.type === "FunctionDeclaration");
1777
+ results
1778
+ .map((r) => r.results)
1779
+ .flat()
1780
+ .filter((c) => c.type === "FunctionDeclaration");
1505
1781
  if (!callees || !callees.length) {
1506
1782
  const n = name ||
1507
1783
  ("name" in node.callee && node.callee.name) ||
@@ -1606,6 +1882,10 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1606
1882
 
1607
1883
 
1608
1884
 
1885
+
1886
+
1887
+
1888
+
1609
1889
  /*
1610
1890
  * This is an unfortunate hack. I want to be able to extract things
1611
1891
  * like the types of all of a Class's variables (in particular the type
@@ -1616,16 +1896,30 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1616
1896
  * but those are at least in a standard format.
1617
1897
  */
1618
1898
  // Extract all enum values from api.mir
1619
- async function api_getApiMapping(state) {
1899
+ async function api_getApiMapping(state, barrelList) {
1620
1900
  // get the path to the currently active sdk
1621
1901
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
1622
1902
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
1903
+ const rezDecl = `module Rez { ${[
1904
+ "Drawables",
1905
+ "Fonts",
1906
+ "JsonData",
1907
+ "Layouts",
1908
+ "Menus",
1909
+ "Strings",
1910
+ ]
1911
+ .map((s) => ` module ${s} {}\n`)
1912
+ .join("")}}`;
1623
1913
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
1624
1914
  .toString()
1625
1915
  .replace(/\r\n/g, "\n")
1626
1916
  .replace(/^\s*\[.*?\]\s*$/gm, "")
1627
1917
  //.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
1628
- .replace(/^(\s*type)\s/gm, "$1def ");
1918
+ .replace(/^(\s*type)\s/gm, "$1def ") +
1919
+ (barrelList || [])
1920
+ .map((name) => `module ${name} { ${rezDecl} }`)
1921
+ .concat(rezDecl)
1922
+ .join("");
1629
1923
  try {
1630
1924
  const result = api_collectNamespaces(parser.parse(api, null, {
1631
1925
  filepath: "api.mir",
@@ -1639,23 +1933,24 @@ async function api_getApiMapping(state) {
1639
1933
  return decls[0];
1640
1934
  }, result);
1641
1935
  const value = api_isStateNode(vs) ? vs.node : vs;
1642
- if (value.type !== "EnumStringMember" &&
1643
- (value.type !== "VariableDeclarator" || value.kind != "const")) {
1936
+ if (!value ||
1937
+ (value.type !== "EnumStringMember" &&
1938
+ (value.type !== "VariableDeclarator" || value.kind != "const"))) {
1644
1939
  throw `Negative constant ${fixup} did not refer to a constant`;
1645
1940
  }
1646
- const init = value.init;
1941
+ const init = getLiteralNode(value.init);
1647
1942
  if (!init || init.type !== "Literal") {
1648
1943
  throw `Negative constant ${fixup} was not a Literal`;
1649
1944
  }
1650
1945
  if (typeof init.value !== "number") {
1651
- console.log(`Negative fixup ${fixup} was already not a number!`);
1946
+ console.log(`Negative fixup ${fixup} was not a number!`);
1652
1947
  }
1653
1948
  else if (init.value > 0) {
1654
1949
  init.value = -init.value;
1655
1950
  init.raw = "-" + init.raw;
1656
1951
  }
1657
1952
  else {
1658
- console.log(`Negative fixup ${fixup} was already negative!`);
1953
+ // console.log(`Negative fixup ${fixup} was already negative!`);
1659
1954
  }
1660
1955
  });
1661
1956
  return result;
@@ -1674,67 +1969,197 @@ function api_isStateNode(node) {
1674
1969
  function api_variableDeclarationName(node) {
1675
1970
  return ("left" in node ? node.left : node).name;
1676
1971
  }
1677
- function checkOne(ns, decls, name) {
1972
+ function lookupToStateNodeDecls(results) {
1973
+ return results.reduce((result, current) => current.results.length
1974
+ ? result
1975
+ ? result.concat(current.results)
1976
+ : current.results
1977
+ : result, null);
1978
+ }
1979
+ function checkOne(state, ns, decls, node, isStatic) {
1980
+ // follow the superchain, looking up node in each class
1981
+ const superChain = (cls) => {
1982
+ if (!cls.superClass || cls.superClass === true) {
1983
+ return null;
1984
+ }
1985
+ return cls.superClass.reduce((result, sup) => {
1986
+ const sdecls = sup[decls];
1987
+ const next = api_hasProperty(sdecls, node.name)
1988
+ ? sdecls[node.name]
1989
+ : superChain(sup);
1990
+ return next ? (result ? result.concat(next) : next) : result;
1991
+ }, null);
1992
+ };
1993
+ const lookupInContext = (ns) => {
1994
+ const [, lkup] = lookup(state, decls, node, null, ns.stack);
1995
+ return lkup && lookupToStateNodeDecls(lkup);
1996
+ };
1997
+ // follow the superchain, looking up node in each class's scope
1998
+ const superChainScopes = (ns) => {
1999
+ const result = lookupInContext(ns);
2000
+ if (result)
2001
+ return result;
2002
+ if (!ns.superClass || ns.superClass === true) {
2003
+ return null;
2004
+ }
2005
+ return ns.superClass.reduce((result, sup) => {
2006
+ const next = superChainScopes(sup);
2007
+ return next ? (result ? result.concat(next) : next) : result;
2008
+ }, null);
2009
+ };
1678
2010
  if (api_isStateNode(ns)) {
1679
- if (api_hasProperty(ns[decls], name)) {
1680
- return ns[decls][name];
1681
- }
1682
- if (ns.type == "ClassDeclaration" &&
1683
- ns.superClass &&
1684
- ns.superClass !== true) {
1685
- const found = ns.superClass
1686
- .map((cls) => checkOne(cls, decls, name))
1687
- .filter((n) => n != null)
1688
- .flat(1);
1689
- return found.length ? found : null;
2011
+ const ndecls = ns[decls];
2012
+ if (api_hasProperty(ndecls, node.name)) {
2013
+ return ndecls[node.name];
2014
+ }
2015
+ switch (ns.type) {
2016
+ case "ClassDeclaration":
2017
+ if (!isStatic) {
2018
+ return superChain(ns) || superChainScopes(ns) || false;
2019
+ }
2020
+ // fall through
2021
+ case "ModuleDeclaration":
2022
+ return lookupInContext(ns) || false;
1690
2023
  }
1691
2024
  }
1692
2025
  return null;
1693
2026
  }
1694
- function lookup(state, decls, node, name, stack) {
1695
- stack || (stack = state.stack);
2027
+ function sameStateNodeDecl(a, b) {
2028
+ if (a === b)
2029
+ return true;
2030
+ if (!a || !b)
2031
+ return false;
2032
+ if (!api_isStateNode(a) || a.type !== b.type)
2033
+ return false;
2034
+ return (a.node === b.node ||
2035
+ a.type === "Program" ||
2036
+ (a.type === "ModuleDeclaration" && a.fullName === b.fullName));
2037
+ }
2038
+ function sameLookupDefinition(a, b) {
2039
+ return (
2040
+ // sameStateNodeDecl(a.parent, b.parent) &&
2041
+ (0,external_util_cjs_namespaceObject.sameArrays)(a.results, b.results, (ar, br) => sameStateNodeDecl(ar, br)));
2042
+ }
2043
+ function api_sameLookupResult(a, b) {
2044
+ return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
2045
+ }
2046
+ /**
2047
+ *
2048
+ * @param state - The ProgramState
2049
+ * @param decls - The field to use to look things up. either "decls" or "type_decls"
2050
+ * @param node - The node to lookup
2051
+ * @param name - Overrides the name of the node.
2052
+ * @param stack - if provided, use this stack, rather than the current
2053
+ * state.stack for the lookup
2054
+ * @param nonlocal - when true, a plain identifier will be looked up as a
2055
+ * non-local. This is needed when looking up a callee.
2056
+ * If the callee is a MemberExpression, the flag is ignored.
2057
+ * @returns
2058
+ * - [string, LookupDefinition[]] - if the lookup succeeds
2059
+ * - [false, false] - if the lookup fails, but its not an error because its not the kind of expression we can lookup
2060
+ * - [null, null] - if the lookup fails unexpectedly.
2061
+ */
2062
+ function lookup(state, decls, node, name, maybeStack, nonlocal) {
2063
+ const stack = maybeStack || state.stack;
1696
2064
  switch (node.type) {
1697
2065
  case "MemberExpression": {
1698
2066
  if (node.property.type != "Identifier" || node.computed)
1699
2067
  break;
1700
- const [, module, where] = lookup(state, decls, node.object, name, stack);
1701
- if (module && module.length === 1) {
1702
- const result = checkOne(module[0], decls, node.property.name);
1703
- if (result) {
1704
- return [
1705
- name || node.property.name,
1706
- result,
1707
- where.concat(module[0]),
1708
- ];
2068
+ const property = node.property;
2069
+ let result;
2070
+ if (node.object.type === "ThisExpression") {
2071
+ [, result] = lookup(state, decls, node.property, name, stack, true);
2072
+ }
2073
+ else {
2074
+ const [, results] = lookup(state, decls, node.object, name, stack, false);
2075
+ if (results === false)
2076
+ break;
2077
+ if (!results)
2078
+ return [null, null];
2079
+ result = results.reduce((current, lookupDef) => {
2080
+ const items = lookupDef.results
2081
+ .map((module) => {
2082
+ const res = checkOne(state, module, decls, property, false);
2083
+ return res ? { parent: module, results: res } : null;
2084
+ })
2085
+ .filter((r) => r != null);
2086
+ if (!items.length)
2087
+ return current;
2088
+ return current ? current.concat(items) : items;
2089
+ }, null);
2090
+ if (!result &&
2091
+ results.some((ld) => ld.results.some((sn) => (api_isStateNode(sn) && sn.fullName?.match(/^\$\.(\w+\.)?Rez\./)) ||
2092
+ sn.type === "VariableDeclarator" ||
2093
+ sn.type === "Identifier" ||
2094
+ sn.type === "BinaryExpression" ||
2095
+ (sn.type === "ClassDeclaration" &&
2096
+ property.name === "initialize")))) {
2097
+ // - The Rez module can contain lots of things from the resource
2098
+ // compiler which we don't track.
2099
+ // - Variables, and formal parameters would require type tracking
2100
+ // which we don't yet do
2101
+ // - Its ok to call an undeclared initialize method.
2102
+ // Report them all as "expected failures".
2103
+ return [false, false];
1709
2104
  }
1710
2105
  }
1711
- break;
2106
+ if (!result)
2107
+ return [null, null];
2108
+ return [name || property.name, result];
1712
2109
  }
1713
2110
  case "ThisExpression": {
1714
- for (let i = stack.length; i--;) {
2111
+ for (let i = stack.length;;) {
1715
2112
  const si = stack[i];
1716
2113
  if (si.type == "ModuleDeclaration" ||
1717
2114
  si.type == "ClassDeclaration" ||
1718
2115
  !i) {
1719
- return [name || si.name, [si], stack.slice(0, i)];
2116
+ return [
2117
+ name || si.name,
2118
+ [{ parent: i ? stack[i - 1] : null, results: [si] }],
2119
+ ];
1720
2120
  }
1721
2121
  }
1722
- break;
1723
2122
  }
1724
2123
  case "Identifier": {
1725
2124
  if (node.name == "$") {
1726
- return [name || node.name, [stack[0]], []];
2125
+ return [name || node.name, [{ parent: null, results: [stack[0]] }]];
1727
2126
  }
2127
+ let inStatic = false;
2128
+ let checkedImports = false;
1728
2129
  for (let i = stack.length; i--;) {
1729
- const result = checkOne(stack[i], decls, node.name);
1730
- if (result) {
1731
- return [name || node.name, result, stack.slice(0, i + 1)];
2130
+ const si = stack[i];
2131
+ switch (si.type) {
2132
+ case "ClassDeclaration":
2133
+ case "ModuleDeclaration":
2134
+ case "Program":
2135
+ if (!checkedImports) {
2136
+ checkedImports = true;
2137
+ const results = findUsingForNode(state, stack, i, node, decls === "type_decls");
2138
+ if (results) {
2139
+ return [name || node.name, [{ parent: si, results }]];
2140
+ }
2141
+ }
2142
+ break;
2143
+ case "FunctionDeclaration":
2144
+ inStatic = si.isStatic === true;
2145
+ // fall through
2146
+ default:
2147
+ if (nonlocal)
2148
+ continue;
2149
+ break;
2150
+ }
2151
+ const results = checkOne(state, si, decls, node, inStatic);
2152
+ if (results) {
2153
+ return [name || node.name, [{ parent: si, results }]];
2154
+ }
2155
+ else if (results === false) {
2156
+ break;
1732
2157
  }
1733
2158
  }
1734
- break;
2159
+ return [null, null];
1735
2160
  }
1736
2161
  }
1737
- return [null, null, null];
2162
+ return [false, false];
1738
2163
  }
1739
2164
  function api_collectNamespaces(ast, stateIn) {
1740
2165
  const state = (stateIn || {});
@@ -1750,14 +2175,15 @@ function api_collectNamespaces(ast, stateIn) {
1750
2175
  let low = 0, high = ast.comments.length;
1751
2176
  while (high > low) {
1752
2177
  const mid = (low + high) >> 1;
1753
- if (ast.comments[mid].start < node.start) {
2178
+ if ((ast.comments[mid].start || 0) < node.start) {
1754
2179
  low = mid + 1;
1755
2180
  }
1756
2181
  else {
1757
2182
  high = mid;
1758
2183
  }
1759
2184
  }
1760
- while (high < ast.comments.length && ast.comments[high].end < node.end) {
2185
+ while (high < ast.comments.length &&
2186
+ (ast.comments[high].end || 0) < node.end) {
1761
2187
  high++;
1762
2188
  }
1763
2189
  if (high > low) {
@@ -1766,10 +2192,14 @@ function api_collectNamespaces(ast, stateIn) {
1766
2192
  }
1767
2193
  };
1768
2194
  state.lookup = (node, name, stack) => lookup(state, state.inType ? "type_decls" : "decls", node, name, stack);
2195
+ state.lookupNonlocal = (node, name, stack) => lookup(state, "decls", node, name, stack, true);
1769
2196
  state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
1770
2197
  state.lookupType = (node, name, stack) => lookup(state, "type_decls", node, name, stack);
2198
+ state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
2199
+ ? { ...elm }
2200
+ : elm);
1771
2201
  state.inType = false;
1772
- state.traverse = (root) => api_traverseAst(root, (node) => {
2202
+ state.traverse = (root) => ast_traverseAst(root, (node) => {
1773
2203
  try {
1774
2204
  if (state.shouldExclude && state.shouldExclude(node)) {
1775
2205
  // don't visit any children, but do call post
@@ -1780,14 +2210,60 @@ function api_collectNamespaces(ast, stateIn) {
1780
2210
  if (state.stack.length != 1) {
1781
2211
  throw new Error("Unexpected stack length for Program node");
1782
2212
  }
2213
+ state.stack[0].node = node;
1783
2214
  break;
1784
2215
  case "TypeSpecList":
1785
2216
  state.inType = true;
1786
2217
  break;
2218
+ case "ImportModule":
2219
+ case "Using": {
2220
+ const [parent] = state.stack.slice(-1);
2221
+ if (!parent.usings) {
2222
+ parent.usings = {};
2223
+ }
2224
+ const name = (node.type === "Using" && node.as && node.as.name) ||
2225
+ (node.id.type === "Identifier"
2226
+ ? node.id.name
2227
+ : node.id.property.name);
2228
+ const using = { node };
2229
+ parent.usings[name] = using;
2230
+ if (node.type == "ImportModule") {
2231
+ if (!parent.imports) {
2232
+ parent.imports = [using];
2233
+ }
2234
+ else {
2235
+ const index = parent.imports.findIndex((using) => (using.node.id.type === "Identifier"
2236
+ ? using.node.id.name
2237
+ : using.node.id.property.name) === name);
2238
+ if (index >= 0)
2239
+ parent.imports.splice(index, 1);
2240
+ parent.imports.push(using);
2241
+ }
2242
+ }
2243
+ break;
2244
+ }
2245
+ case "CatchClause":
2246
+ if (node.param) {
2247
+ const [parent] = state.stack.slice(-1);
2248
+ if (!parent.decls)
2249
+ parent.decls = {};
2250
+ const id = node.param.type === "Identifier"
2251
+ ? node.param
2252
+ : node.param.left;
2253
+ state.stack.push({
2254
+ type: "BlockStatement",
2255
+ fullName: undefined,
2256
+ name: undefined,
2257
+ node: node.body,
2258
+ decls: { [id.name]: [id] },
2259
+ });
2260
+ }
2261
+ break;
1787
2262
  case "BlockStatement": {
1788
2263
  const [parent] = state.stack.slice(-1);
1789
- if (parent.type != "FunctionDeclaration" &&
1790
- parent.type != "BlockStatement") {
2264
+ if (parent.node === node ||
2265
+ (parent.type != "FunctionDeclaration" &&
2266
+ parent.type != "BlockStatement")) {
1791
2267
  break;
1792
2268
  }
1793
2269
  // fall through
@@ -1833,6 +2309,9 @@ function api_collectNamespaces(ast, stateIn) {
1833
2309
  parent.decls[name].push(elm);
1834
2310
  if (node.type == "ModuleDeclaration" ||
1835
2311
  node.type == "ClassDeclaration") {
2312
+ // Inject the class/module name into itself,
2313
+ // so you can say Graphics.Graphics.Graphics.COLOR_RED
2314
+ elm.decls = { [name]: [elm] };
1836
2315
  if (!parent.type_decls)
1837
2316
  parent.type_decls = {};
1838
2317
  if (!api_hasProperty(parent.type_decls, name)) {
@@ -1880,7 +2359,7 @@ function api_collectNamespaces(ast, stateIn) {
1880
2359
  if (!parent.decls)
1881
2360
  parent.decls = {};
1882
2361
  const decls = parent.decls;
1883
- const stack = state.stack.slice();
2362
+ const stack = state.stackClone();
1884
2363
  node.declarations.forEach((decl) => {
1885
2364
  const name = api_variableDeclarationName(decl.id);
1886
2365
  if (!api_hasProperty(decls, name)) {
@@ -1984,8 +2463,14 @@ function api_collectNamespaces(ast, stateIn) {
1984
2463
  state.inType = true;
1985
2464
  break;
1986
2465
  }
1987
- if (state.stack.slice(-1).pop()?.node === node) {
1988
- state.stack.pop();
2466
+ const [parent] = state.stack.slice(-1);
2467
+ if (parent.node === node ||
2468
+ (node.type === "CatchClause" && parent.node === node.body)) {
2469
+ delete parent.usings;
2470
+ delete parent.imports;
2471
+ if (node.type != "Program") {
2472
+ state.stack.pop();
2473
+ }
1989
2474
  }
1990
2475
  }
1991
2476
  if (ret === false) {
@@ -2006,68 +2491,7 @@ function api_collectNamespaces(ast, stateIn) {
2006
2491
  }
2007
2492
  return state.stack[0];
2008
2493
  }
2009
- function isMCTreeNode(node) {
2010
- return node ? typeof node === "object" && "type" in node : false;
2011
- }
2012
- /*
2013
- * Traverse the ast rooted at node, calling pre before
2014
- * visiting each node, and post after.
2015
- *
2016
- * - if pre returns false, the node is not traversed, and
2017
- * post is not called;
2018
- * - if pre returns a list of child nodes, only those will
2019
- * be traversed
2020
- * - otherwise all child nodes are traversed
2021
- *
2022
- * - if post returns false, the node it was called on is
2023
- * removed.
2024
- */
2025
- function api_traverseAst(node, pre, post) {
2026
- const nodes = pre && pre(node);
2027
- if (nodes === false)
2028
- return;
2029
- for (const key of nodes || Object.keys(node)) {
2030
- const value = node[key];
2031
- if (!value)
2032
- continue;
2033
- if (Array.isArray(value)) {
2034
- const values = value;
2035
- const deletions = values.reduce((state, obj, i) => {
2036
- if (isMCTreeNode(obj)) {
2037
- const repl = api_traverseAst(obj, pre, post);
2038
- if (repl === false) {
2039
- if (!state)
2040
- state = {};
2041
- state[i] = true;
2042
- }
2043
- else if (repl != null) {
2044
- if (!state)
2045
- state = {};
2046
- values[i] = repl;
2047
- }
2048
- }
2049
- return state;
2050
- }, null);
2051
- if (deletions) {
2052
- values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
2053
- }
2054
- }
2055
- else if (isMCTreeNode(value)) {
2056
- const repl = api_traverseAst(value, pre, post);
2057
- if (repl === false) {
2058
- delete node[key];
2059
- }
2060
- else if (repl != null) {
2061
- if (Array.isArray(repl)) {
2062
- throw new Error("Array returned by traverseAst in Node context");
2063
- }
2064
- node[key] = repl;
2065
- }
2066
- }
2067
- }
2068
- return post && post(node);
2069
- }
2070
- function formatAst(node, monkeyCSource = null) {
2494
+ function api_formatAst(node, monkeyCSource = null) {
2071
2495
  /*
2072
2496
  * The estree printer sometimes looks at the parent node without
2073
2497
  * checking that there *is* a parent node (eg it assumes all
@@ -2108,7 +2532,7 @@ function handleException(state, node, exception) {
2108
2532
  .filter((e) => e != null)
2109
2533
  .join(".");
2110
2534
  const location = node.loc && node.loc.source
2111
- ? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
2535
+ ? `${node.loc.source}:${node.loc.start.line || 0}:${node.loc.end.line || 0}`
2112
2536
  : "<unknown>";
2113
2537
  const message = `Got exception \`${exception instanceof Error
2114
2538
  ? exception.message
@@ -2120,9 +2544,65 @@ function handleException(state, node, exception) {
2120
2544
  exception = new Error(message);
2121
2545
  }
2122
2546
  }
2123
- finally {
2547
+ catch (ex) {
2124
2548
  throw exception;
2125
2549
  }
2550
+ throw exception;
2551
+ }
2552
+ function findUsing(state, stack, using) {
2553
+ if (using.module)
2554
+ return using.module;
2555
+ let module = stack[0];
2556
+ const find = (node) => {
2557
+ let name;
2558
+ if (node.type == "Identifier") {
2559
+ name = node.name;
2560
+ }
2561
+ else {
2562
+ find(node.object);
2563
+ name = node.property.name;
2564
+ }
2565
+ if (api_hasProperty(module.decls, name)) {
2566
+ const decls = module.decls[name];
2567
+ if (decls &&
2568
+ decls.length === 1 &&
2569
+ decls[0].type === "ModuleDeclaration") {
2570
+ module = decls[0];
2571
+ return true;
2572
+ }
2573
+ }
2574
+ return false;
2575
+ };
2576
+ if (find(using.node.id)) {
2577
+ using.module = module;
2578
+ return using.module;
2579
+ }
2580
+ if (state.config?.checkInvalidSymbols !== "OFF") {
2581
+ inliner_diagnostic(state, using.node.id.loc, `Unable to resolve import of ${api_formatAst(using.node.id)}`, state.config?.checkInvalidSymbols || "WARNING");
2582
+ }
2583
+ return null;
2584
+ }
2585
+ function findUsingForNode(state, stack, i, node, isType) {
2586
+ while (i >= 0) {
2587
+ const si = stack[i--];
2588
+ if (api_hasProperty(si.usings, node.name)) {
2589
+ const using = si.usings[node.name];
2590
+ const module = findUsing(state, stack, using);
2591
+ return module && [module];
2592
+ }
2593
+ if (si.imports && isType) {
2594
+ for (let j = si.imports.length; j--;) {
2595
+ const using = si.imports[j];
2596
+ const module = findUsing(state, stack, using);
2597
+ if (module) {
2598
+ if (api_hasProperty(module.type_decls, node.name)) {
2599
+ return module.type_decls[node.name];
2600
+ }
2601
+ }
2602
+ }
2603
+ }
2604
+ }
2605
+ return null;
2126
2606
  }
2127
2607
 
2128
2608
  var __webpack_export_target__ = exports;