@markw65/monkeyc-optimizer 1.0.20 → 1.0.23

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: ["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,27 +269,37 @@ 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.
134
276
  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")
277
+ switch (decl.type) {
278
+ // enums are constant, they cant change
279
+ case "EnumStringMember":
141
280
  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")
281
+ case "VariableDeclarator": {
282
+ // constants also can't change
283
+ if (decl.node.kind === "const")
148
284
  return true;
285
+ // if decl is a local, it also can't be changed
286
+ // by a call to another function.
287
+ for (let i = 0;; i++) {
288
+ if (!state.stack[i] || decl.stack[i] !== state.stack[i])
289
+ return false;
290
+ if (state.stack[i].type === "FunctionDeclaration")
291
+ return true;
292
+ }
149
293
  }
294
+ case "Identifier":
295
+ case "BinaryExpression":
296
+ // This is a parameter of the calling function.
297
+ // It also can't be changed during the execution
298
+ // of the inlined function
299
+ return true;
300
+ default:
301
+ return null;
150
302
  }
151
- return null;
152
303
  };
153
304
  const safeArgs = [];
154
305
  let allSafe = true;
@@ -160,11 +311,13 @@ function getArgSafety(state, func, args, requireAll) {
160
311
  case "Identifier":
161
312
  case "MemberExpression": {
162
313
  const [, results] = state.lookup(arg);
163
- if (!results || results.length !== 1) {
314
+ if (!results ||
315
+ results.length !== 1 ||
316
+ results[0].results.length !== 1) {
164
317
  safeArgs.push(null);
165
318
  return !requireAll;
166
319
  }
167
- const safety = getSafety(results[0]);
320
+ const safety = getSafety(results[0].results[0]);
168
321
  safeArgs.push(safety);
169
322
  if (!safety) {
170
323
  allSafe = false;
@@ -184,25 +337,12 @@ function getArgSafety(state, func, args, requireAll) {
184
337
  if (allSafe && requireAll)
185
338
  return true;
186
339
  let callSeen = false;
187
- let ok = true;
188
340
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
189
- const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
190
341
  // look for uses of "unsafe" args that occur after a call.
191
342
  // use post to do the checking, because arguments are evaluated
192
343
  // prior to the call, so eg "return f(x.y);" is fine, but
193
344
  // "return f()+x.y" is not.
194
- //
195
- // We also have to use a "pre" to ensure that child nodes are
196
- // visited in source order (otherwise we could visit x.y before f()
197
- // in the above example)
198
- traverseAst(func.node.body, (node) => {
199
- return Object.entries(node)
200
- .filter((kv) => Array.isArray(kv[1])
201
- ? kv[1].length !== 0 && hasProperty(kv[1][0], "type")
202
- : hasProperty(kv[1], "type"))
203
- .sort(([, a], [, b]) => getLoc(a) - getLoc(b))
204
- .map(([key]) => key);
205
- }, (node) => {
345
+ traverseAst(func.node.body, null, (node) => {
206
346
  switch (node.type) {
207
347
  case "AssignmentExpression":
208
348
  case "UpdateExpression": {
@@ -256,27 +396,23 @@ function inliningLooksUseful(func, node) {
256
396
  }
257
397
  return false;
258
398
  }
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
399
  function inlineRequested(state, func) {
266
400
  const excludeAnnotations = (func.node.loc?.source &&
267
401
  state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
268
402
  {};
269
403
  if (func.node.attrs &&
270
- func.node.attrs.attrs &&
271
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
404
+ func.node.attrs.attributes &&
405
+ func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
272
406
  (attr.argument.name === "inline" ||
273
407
  (attr.argument.name.startsWith("inline_") &&
274
- hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
408
+ !hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
275
409
  return true;
276
410
  }
277
411
  return false;
278
412
  }
279
413
  function inliner_shouldInline(state, func, call, context) {
414
+ if (state.inlining)
415
+ return false;
280
416
  let autoInline = false;
281
417
  let inlineAsExpression = false;
282
418
  const args = call.arguments;
@@ -309,6 +445,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
309
445
  let failed = false;
310
446
  const pre = state.pre;
311
447
  const post = state.post;
448
+ state.inlining = true;
312
449
  try {
313
450
  state.pre = (node) => {
314
451
  if (failed)
@@ -354,7 +491,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
354
491
  };
355
492
  state.post = (node) => {
356
493
  if (failed)
357
- return null;
494
+ return post(node, state);
358
495
  let replacement = null;
359
496
  switch (node.type) {
360
497
  case "Identifier": {
@@ -371,12 +508,13 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
371
508
  if (!replacement) {
372
509
  failed = true;
373
510
  inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
374
- return null;
511
+ return post(node, state);
375
512
  }
376
513
  break;
377
514
  }
378
515
  }
379
- return post(replacement || node, state) || replacement;
516
+ const ret = post(replacement || node, state);
517
+ return ret === false || ret ? ret : replacement;
380
518
  };
381
519
  let ret = state.traverse(root);
382
520
  if (failed) {
@@ -395,6 +533,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
395
533
  finally {
396
534
  state.pre = pre;
397
535
  state.post = post;
536
+ delete state.inlining;
398
537
  }
399
538
  }
400
539
  function inliner_unused(expression, top) {
@@ -436,13 +575,13 @@ function inliner_unused(expression, top) {
436
575
  },
437
576
  ];
438
577
  }
439
- function diagnostic(state, loc, message) {
578
+ function inliner_diagnostic(state, loc, message, type = "INFO") {
440
579
  if (!loc || !loc.source)
441
580
  return;
442
581
  const source = loc.source;
443
582
  if (!state.diagnostics)
444
583
  state.diagnostics = {};
445
- if (!hasProperty(state.diagnostics, source)) {
584
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
446
585
  if (!message)
447
586
  return;
448
587
  state.diagnostics[source] = [];
@@ -452,7 +591,7 @@ function diagnostic(state, loc, message) {
452
591
  if (message) {
453
592
  if (index < 0)
454
593
  index = diags.length;
455
- diags[index] = { type: "INFO", loc, message };
594
+ diags[index] = { type, loc, message };
456
595
  }
457
596
  else if (index >= 0) {
458
597
  diags.splice(index, 1);
@@ -460,7 +599,7 @@ function diagnostic(state, loc, message) {
460
599
  }
461
600
  function inlineDiagnostic(state, func, call, message) {
462
601
  if (inlineRequested(state, func)) {
463
- 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}`);
464
603
  }
465
604
  }
466
605
  function inlineWithArgs(state, func, call, context) {
@@ -508,7 +647,7 @@ function inlineWithArgs(state, func, call, context) {
508
647
  if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
509
648
  return null;
510
649
  }
511
- diagnostic(state, call.loc, null);
650
+ inliner_diagnostic(state, call.loc, null);
512
651
  if (context.type !== "ReturnStatement" && retStmtCount) {
513
652
  const last = body.body[body.body.length - 1];
514
653
  if (last.type != "ReturnStatement") {
@@ -571,23 +710,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
571
710
  // With a bit more work, we could find the guaranteed shortest
572
711
  // reference, and then use this to optimize *all* symbols, not
573
712
  // just fix inlined ones.
574
- if (current &&
575
- current.length === original.length &&
576
- current.every((item, index) => item == original[index])) {
713
+ if (current && sameLookupResult(original, current)) {
577
714
  return lookupNode;
578
715
  }
579
716
  const node = lookupNode.type === "Identifier"
580
717
  ? lookupNode
581
718
  : lookupNode.property;
582
- if (original.length === 1 && original[0].type === "EnumStringMember") {
583
- 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);
584
723
  }
585
- const prefixes = original.map((sn) => {
724
+ const prefixes = original
725
+ .map((lookupDef) => lookupDef.results.map((sn) => {
586
726
  if (isStateNode(sn) && sn.fullName) {
587
727
  return sn.fullName;
588
728
  }
589
729
  return "";
590
- });
730
+ }))
731
+ .flat();
591
732
  if (prefixes.length &&
592
733
  prefixes[0].startsWith("$.") &&
593
734
  prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
@@ -597,9 +738,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
597
738
  if (found)
598
739
  return current;
599
740
  const [, results] = state.lookup(current);
600
- if (results &&
601
- results.length === original.length &&
602
- results.every((result, i) => result === original[i])) {
741
+ if (results && sameLookupResult(original, results)) {
603
742
  found = true;
604
743
  return current;
605
744
  }
@@ -641,6 +780,99 @@ function fixNodeScope(state, lookupNode, nodeStack) {
641
780
 
642
781
  ;// CONCATENATED MODULE: external "./util.cjs"
643
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
+
644
876
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
645
877
 
646
878
 
@@ -648,43 +880,25 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
648
880
 
649
881
 
650
882
 
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
- }
883
+
884
+
682
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"];
683
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
+ }
684
895
  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");
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");
688
902
  // set it "true" if there is a superClass, but we can't find it.
689
903
  elm.superClass = superClass && superClass.length ? superClass : true;
690
904
  if (name && elm.superClass !== true) {
@@ -716,6 +930,9 @@ function collectClassInfo(state) {
716
930
  elm.decls[name] = elm.superClass;
717
931
  }
718
932
  }
933
+ else if (elm !== object[0]) {
934
+ elm.superClass = object;
935
+ }
719
936
  });
720
937
  const markOverrides = (cls, scls) => {
721
938
  if (scls === true)
@@ -746,7 +963,9 @@ function getFileSources(fnMap) {
746
963
  fs
747
964
  .readFile(name)
748
965
  .then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
749
- })).then(() => { });
966
+ })).then(() => {
967
+ return;
968
+ });
750
969
  }
751
970
  function getFileASTs(fnMap) {
752
971
  return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
@@ -769,22 +988,23 @@ function getFileASTs(fnMap) {
769
988
  return ok;
770
989
  }, true));
771
990
  }
772
- async function analyze(fnMap) {
991
+ async function analyze(fnMap, barrelList, config) {
773
992
  let hasTests = false;
774
- const allImports = [];
993
+ let markApi = true;
775
994
  const preState = {
776
995
  fnMap,
996
+ config,
777
997
  allFunctions: [],
778
998
  allClasses: [],
779
999
  shouldExclude(node) {
780
1000
  if ("attrs" in node &&
781
1001
  node.attrs &&
782
- "attrs" in node.attrs &&
783
- node.attrs.attrs &&
1002
+ "attributes" in node.attrs &&
1003
+ node.attrs.attributes &&
784
1004
  node.loc?.source) {
785
1005
  const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
786
1006
  if (excludeAnnotations) {
787
- return node.attrs.attrs.reduce((drop, attr) => {
1007
+ return node.attrs.attributes.elements.reduce((drop, attr) => {
788
1008
  if (attr.type != "UnaryExpression")
789
1009
  return drop;
790
1010
  if (attr.argument.type != "Identifier")
@@ -801,45 +1021,38 @@ async function analyze(fnMap) {
801
1021
  }
802
1022
  return false;
803
1023
  },
804
- post(node, state) {
1024
+ pre(node, state) {
805
1025
  switch (node.type) {
806
1026
  case "FunctionDeclaration":
1027
+ if (markApi) {
1028
+ node.body = null;
1029
+ break;
1030
+ }
1031
+ // falls through
1032
+ case "ModuleDeclaration":
807
1033
  case "ClassDeclaration": {
808
1034
  const [scope] = state.stack.slice(-1);
809
- const stack = state.stack.slice(0, -1);
810
- scope.stack = stack;
1035
+ scope.stack = state.stackClone().slice(0, -1);
811
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"));
812
1042
  state.allFunctions.push(scope);
813
1043
  }
814
- else {
1044
+ else if (scope.type === "ClassDeclaration") {
815
1045
  state.allClasses.push(scope);
816
1046
  }
817
- return null;
1047
+ break;
818
1048
  }
819
- case "Using":
820
- case "ImportModule":
821
- allImports.push({ node, stack: state.stack.slice() });
822
- return null;
823
- default:
824
- return null;
825
1049
  }
1050
+ return null;
826
1051
  },
827
1052
  };
828
- await getApiMapping(preState);
1053
+ await getApiMapping(preState, barrelList);
1054
+ markApi = false;
829
1055
  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
1056
  await getFileASTs(fnMap);
844
1057
  Object.entries(fnMap).forEach(([name, value]) => {
845
1058
  const { ast, parserError } = value;
@@ -852,8 +1065,28 @@ async function analyze(fnMap) {
852
1065
  });
853
1066
  delete state.shouldExclude;
854
1067
  delete state.post;
855
- processImports(allImports, state.lookup);
856
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
+ }
857
1090
  return state;
858
1091
  }
859
1092
  function compareLiteralLike(a, b) {
@@ -863,11 +1096,11 @@ function compareLiteralLike(a, b) {
863
1096
  b = b.left;
864
1097
  return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
865
1098
  }
866
- function getLiteralFromDecls(decls) {
867
- if (!decls.length)
1099
+ function getLiteralFromDecls(lookupDefns) {
1100
+ if (!lookupDefns.length)
868
1101
  return null;
869
1102
  let result = null;
870
- if (decls.every((d) => {
1103
+ if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
871
1104
  if (d.type === "EnumStringMember" ||
872
1105
  (d.type === "VariableDeclarator" && d.node.kind === "const")) {
873
1106
  const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
@@ -882,7 +1115,7 @@ function getLiteralFromDecls(decls) {
882
1115
  }
883
1116
  }
884
1117
  return false;
885
- })) {
1118
+ }))) {
886
1119
  return result;
887
1120
  }
888
1121
  return null;
@@ -1093,14 +1326,23 @@ function markFunctionCalled(state, func) {
1093
1326
  }
1094
1327
  pushUnique(state.calledFunctions[func.id.name], func);
1095
1328
  }
1096
- async function optimizeMonkeyC(fnMap) {
1329
+ async function optimizeMonkeyC(fnMap, barrelList, config) {
1097
1330
  const state = {
1098
- ...(await analyze(fnMap)),
1331
+ ...(await analyze(fnMap, barrelList, config)),
1099
1332
  localsStack: [{}],
1100
1333
  exposed: {},
1101
1334
  calledFunctions: {},
1102
1335
  };
1103
- const replace = (node, obj) => {
1336
+ const replace = (node, old) => {
1337
+ if (node === false || node === null)
1338
+ return node;
1339
+ const rep = state.traverse(node);
1340
+ if (rep === false || Array.isArray(rep))
1341
+ return rep;
1342
+ return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
1343
+ };
1344
+ const inPlaceReplacement = (node, obj) => {
1345
+ const { start, end, loc } = node;
1104
1346
  for (const k of Object.keys(node)) {
1105
1347
  delete node[k];
1106
1348
  }
@@ -1108,13 +1350,16 @@ async function optimizeMonkeyC(fnMap) {
1108
1350
  obj = {
1109
1351
  type: "BinaryExpression",
1110
1352
  operator: "as",
1111
- left: obj,
1353
+ left: { ...obj, start, end, loc },
1112
1354
  right: { type: "TypeSpecList", ts: [obj.enumType] },
1113
1355
  };
1114
1356
  }
1115
1357
  for (const [k, v] of Object.entries(obj)) {
1116
1358
  node[k] = v;
1117
1359
  }
1360
+ node.loc = loc;
1361
+ node.start = start;
1362
+ node.end = end;
1118
1363
  };
1119
1364
  const lookupAndReplace = (node) => {
1120
1365
  const [, objects] = state.lookup(node);
@@ -1125,7 +1370,7 @@ async function optimizeMonkeyC(fnMap) {
1125
1370
  if (!obj) {
1126
1371
  return false;
1127
1372
  }
1128
- replace(node, obj);
1373
+ inPlaceReplacement(node, obj);
1129
1374
  return true;
1130
1375
  };
1131
1376
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
@@ -1141,8 +1386,8 @@ async function optimizeMonkeyC(fnMap) {
1141
1386
  if (hasProperty(state.exposed, func.id.name))
1142
1387
  return true;
1143
1388
  if (func.attrs &&
1144
- func.attrs.attrs &&
1145
- func.attrs.attrs.some((attr) => {
1389
+ func.attrs.attributes &&
1390
+ func.attrs.attributes.elements.some((attr) => {
1146
1391
  if (attr.type != "UnaryExpression")
1147
1392
  return false;
1148
1393
  if (attr.argument.type != "Identifier")
@@ -1172,7 +1417,7 @@ async function optimizeMonkeyC(fnMap) {
1172
1417
  case "ConditionalExpression":
1173
1418
  case "IfStatement":
1174
1419
  case "DoWhileStatement":
1175
- case "WhileStatement":
1420
+ case "WhileStatement": {
1176
1421
  const test = (state.traverse(node.test) ||
1177
1422
  node.test);
1178
1423
  const [value, type] = getNodeValue(test);
@@ -1204,6 +1449,7 @@ async function optimizeMonkeyC(fnMap) {
1204
1449
  }
1205
1450
  }
1206
1451
  return null;
1452
+ }
1207
1453
  case "EnumDeclaration":
1208
1454
  return false;
1209
1455
  case "ForStatement": {
@@ -1233,6 +1479,37 @@ async function optimizeMonkeyC(fnMap) {
1233
1479
  }
1234
1480
  return ["init"];
1235
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;
1236
1513
  case "UnaryExpression":
1237
1514
  if (node.operator == ":") {
1238
1515
  // If we produce a Symbol, for a given name,
@@ -1315,8 +1592,7 @@ async function optimizeMonkeyC(fnMap) {
1315
1592
  }
1316
1593
  const opt = optimizeNode(node);
1317
1594
  if (opt) {
1318
- replace(node, opt);
1319
- return null;
1595
+ return replace(opt, node);
1320
1596
  }
1321
1597
  switch (node.type) {
1322
1598
  case "ConditionalExpression":
@@ -1326,7 +1602,7 @@ async function optimizeMonkeyC(fnMap) {
1326
1602
  const rep = node.test.value ? node.consequent : node.alternate;
1327
1603
  if (!rep)
1328
1604
  return false;
1329
- replace(node, rep);
1605
+ return replace(rep, rep);
1330
1606
  }
1331
1607
  break;
1332
1608
  case "WhileStatement":
@@ -1341,15 +1617,11 @@ async function optimizeMonkeyC(fnMap) {
1341
1617
  break;
1342
1618
  case "ReturnStatement":
1343
1619
  if (node.argument && node.argument.type === "CallExpression") {
1344
- return optimizeCall(state, node.argument, node);
1620
+ return replace(optimizeCall(state, node.argument, node), node.argument);
1345
1621
  }
1346
1622
  break;
1347
1623
  case "CallExpression": {
1348
- const ret = optimizeCall(state, node, null);
1349
- if (ret) {
1350
- replace(node, ret);
1351
- }
1352
- break;
1624
+ return replace(optimizeCall(state, node, null), node);
1353
1625
  }
1354
1626
  case "AssignmentExpression":
1355
1627
  if (node.operator === "=" &&
@@ -1361,7 +1633,7 @@ async function optimizeMonkeyC(fnMap) {
1361
1633
  break;
1362
1634
  case "ExpressionStatement":
1363
1635
  if (node.expression.type === "CallExpression") {
1364
- return optimizeCall(state, node.expression, node);
1636
+ return replace(optimizeCall(state, node.expression, node), node.expression);
1365
1637
  }
1366
1638
  else if (node.expression.type === "AssignmentExpression") {
1367
1639
  if (node.expression.right.type === "CallExpression") {
@@ -1373,14 +1645,10 @@ async function optimizeMonkeyC(fnMap) {
1373
1645
  }
1374
1646
  if (!ok && node.expression.operator == "=") {
1375
1647
  const [, result] = state.lookup(node.expression.left);
1376
- ok = result != null;
1648
+ ok = !!result;
1377
1649
  }
1378
1650
  if (ok) {
1379
- const ret = optimizeCall(state, node.expression.right, node.expression);
1380
- if (ret && ret.type === "BlockStatement") {
1381
- const r2 = state.traverse(ret);
1382
- return r2 === false || r2 ? r2 : ret;
1383
- }
1651
+ return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
1384
1652
  }
1385
1653
  }
1386
1654
  }
@@ -1388,12 +1656,9 @@ async function optimizeMonkeyC(fnMap) {
1388
1656
  const ret = unused(node.expression, true);
1389
1657
  if (ret) {
1390
1658
  return ret
1391
- .map((s) => {
1392
- const r2 = state.traverse(s);
1393
- return r2 === false || r2 ? r2 : s;
1394
- })
1659
+ .map((r) => replace(r, r))
1395
1660
  .flat(1)
1396
- .filter((s) => s !== false);
1661
+ .filter((s) => !!s);
1397
1662
  }
1398
1663
  }
1399
1664
  break;
@@ -1412,6 +1677,9 @@ async function optimizeMonkeyC(fnMap) {
1412
1677
  delete state.post;
1413
1678
  const cleanup = (node) => {
1414
1679
  switch (node.type) {
1680
+ case "ThisExpression":
1681
+ node.text = "self";
1682
+ break;
1415
1683
  case "EnumStringBody":
1416
1684
  if (node.members.every((m) => {
1417
1685
  const name = "name" in m ? m.name : m.id.name;
@@ -1436,19 +1704,24 @@ async function optimizeMonkeyC(fnMap) {
1436
1704
  if (!node.body.members.length) {
1437
1705
  if (!node.id)
1438
1706
  return false;
1439
- if (!node.body["enumType"]) {
1707
+ if (!node.body.enumType) {
1440
1708
  throw new Error("Missing enumType on optimized enum");
1441
1709
  }
1442
- replace(node, {
1710
+ return {
1443
1711
  type: "TypedefDeclaration",
1444
1712
  id: node.id,
1445
1713
  ts: {
1446
1714
  type: "UnaryExpression",
1447
- argument: { type: "TypeSpecList", ts: [node.body.enumType] },
1715
+ argument: {
1716
+ type: "TypeSpecList",
1717
+ ts: [
1718
+ node.body.enumType,
1719
+ ],
1720
+ },
1448
1721
  prefix: true,
1449
1722
  operator: " as",
1450
1723
  },
1451
- });
1724
+ };
1452
1725
  }
1453
1726
  break;
1454
1727
  case "VariableDeclaration": {
@@ -1471,6 +1744,19 @@ async function optimizeMonkeyC(fnMap) {
1471
1744
  return false;
1472
1745
  }
1473
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
+ }
1474
1760
  }
1475
1761
  return null;
1476
1762
  };
@@ -1486,9 +1772,12 @@ async function optimizeMonkeyC(fnMap) {
1486
1772
  return state.diagnostics;
1487
1773
  }
1488
1774
  function optimizeCall(state, node, context) {
1489
- const [name, results] = state.lookup(node.callee);
1775
+ const [name, results] = state.lookupNonlocal(node.callee);
1490
1776
  const callees = results &&
1491
- results.filter((c) => c.type === "FunctionDeclaration");
1777
+ results
1778
+ .map((r) => r.results)
1779
+ .flat()
1780
+ .filter((c) => c.type === "FunctionDeclaration");
1492
1781
  if (!callees || !callees.length) {
1493
1782
  const n = name ||
1494
1783
  ("name" in node.callee && node.callee.name) ||
@@ -1593,6 +1882,10 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1593
1882
 
1594
1883
 
1595
1884
 
1885
+
1886
+
1887
+
1888
+
1596
1889
  /*
1597
1890
  * This is an unfortunate hack. I want to be able to extract things
1598
1891
  * like the types of all of a Class's variables (in particular the type
@@ -1603,16 +1896,30 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1603
1896
  * but those are at least in a standard format.
1604
1897
  */
1605
1898
  // Extract all enum values from api.mir
1606
- async function api_getApiMapping(state) {
1899
+ async function api_getApiMapping(state, barrelList) {
1607
1900
  // get the path to the currently active sdk
1608
1901
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
1609
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("")}}`;
1610
1913
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
1611
1914
  .toString()
1612
1915
  .replace(/\r\n/g, "\n")
1613
1916
  .replace(/^\s*\[.*?\]\s*$/gm, "")
1614
1917
  //.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
1615
- .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("");
1616
1923
  try {
1617
1924
  const result = api_collectNamespaces(parser.parse(api, null, {
1618
1925
  filepath: "api.mir",
@@ -1626,23 +1933,24 @@ async function api_getApiMapping(state) {
1626
1933
  return decls[0];
1627
1934
  }, result);
1628
1935
  const value = api_isStateNode(vs) ? vs.node : vs;
1629
- if (value.type !== "EnumStringMember" &&
1630
- (value.type !== "VariableDeclarator" || value.kind != "const")) {
1936
+ if (!value ||
1937
+ (value.type !== "EnumStringMember" &&
1938
+ (value.type !== "VariableDeclarator" || value.kind != "const"))) {
1631
1939
  throw `Negative constant ${fixup} did not refer to a constant`;
1632
1940
  }
1633
- const init = value.init;
1941
+ const init = getLiteralNode(value.init);
1634
1942
  if (!init || init.type !== "Literal") {
1635
1943
  throw `Negative constant ${fixup} was not a Literal`;
1636
1944
  }
1637
1945
  if (typeof init.value !== "number") {
1638
- console.log(`Negative fixup ${fixup} was already not a number!`);
1946
+ console.log(`Negative fixup ${fixup} was not a number!`);
1639
1947
  }
1640
1948
  else if (init.value > 0) {
1641
1949
  init.value = -init.value;
1642
1950
  init.raw = "-" + init.raw;
1643
1951
  }
1644
1952
  else {
1645
- console.log(`Negative fixup ${fixup} was already negative!`);
1953
+ // console.log(`Negative fixup ${fixup} was already negative!`);
1646
1954
  }
1647
1955
  });
1648
1956
  return result;
@@ -1661,67 +1969,197 @@ function api_isStateNode(node) {
1661
1969
  function api_variableDeclarationName(node) {
1662
1970
  return ("left" in node ? node.left : node).name;
1663
1971
  }
1664
- 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
+ };
1665
2010
  if (api_isStateNode(ns)) {
1666
- if (api_hasProperty(ns[decls], name)) {
1667
- return ns[decls][name];
1668
- }
1669
- if (ns.type == "ClassDeclaration" &&
1670
- ns.superClass &&
1671
- ns.superClass !== true) {
1672
- const found = ns.superClass
1673
- .map((cls) => checkOne(cls, decls, name))
1674
- .filter((n) => n != null)
1675
- .flat(1);
1676
- 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;
1677
2023
  }
1678
2024
  }
1679
2025
  return null;
1680
2026
  }
1681
- function lookup(state, decls, node, name, stack) {
1682
- 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;
1683
2064
  switch (node.type) {
1684
2065
  case "MemberExpression": {
1685
2066
  if (node.property.type != "Identifier" || node.computed)
1686
2067
  break;
1687
- const [, module, where] = lookup(state, decls, node.object, name, stack);
1688
- if (module && module.length === 1) {
1689
- const result = checkOne(module[0], decls, node.property.name);
1690
- if (result) {
1691
- return [
1692
- name || node.property.name,
1693
- result,
1694
- where.concat(module[0]),
1695
- ];
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];
1696
2104
  }
1697
2105
  }
1698
- break;
2106
+ if (!result)
2107
+ return [null, null];
2108
+ return [name || property.name, result];
1699
2109
  }
1700
2110
  case "ThisExpression": {
1701
- for (let i = stack.length; i--;) {
2111
+ for (let i = stack.length;;) {
1702
2112
  const si = stack[i];
1703
2113
  if (si.type == "ModuleDeclaration" ||
1704
2114
  si.type == "ClassDeclaration" ||
1705
2115
  !i) {
1706
- 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
+ ];
1707
2120
  }
1708
2121
  }
1709
- break;
1710
2122
  }
1711
2123
  case "Identifier": {
1712
2124
  if (node.name == "$") {
1713
- return [name || node.name, [stack[0]], []];
2125
+ return [name || node.name, [{ parent: null, results: [stack[0]] }]];
1714
2126
  }
2127
+ let inStatic = false;
2128
+ let checkedImports = false;
1715
2129
  for (let i = stack.length; i--;) {
1716
- const result = checkOne(stack[i], decls, node.name);
1717
- if (result) {
1718
- 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;
1719
2157
  }
1720
2158
  }
1721
- break;
2159
+ return [null, null];
1722
2160
  }
1723
2161
  }
1724
- return [null, null, null];
2162
+ return [false, false];
1725
2163
  }
1726
2164
  function api_collectNamespaces(ast, stateIn) {
1727
2165
  const state = (stateIn || {});
@@ -1737,14 +2175,15 @@ function api_collectNamespaces(ast, stateIn) {
1737
2175
  let low = 0, high = ast.comments.length;
1738
2176
  while (high > low) {
1739
2177
  const mid = (low + high) >> 1;
1740
- if (ast.comments[mid].start < node.start) {
2178
+ if ((ast.comments[mid].start || 0) < node.start) {
1741
2179
  low = mid + 1;
1742
2180
  }
1743
2181
  else {
1744
2182
  high = mid;
1745
2183
  }
1746
2184
  }
1747
- 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) {
1748
2187
  high++;
1749
2188
  }
1750
2189
  if (high > low) {
@@ -1753,10 +2192,14 @@ function api_collectNamespaces(ast, stateIn) {
1753
2192
  }
1754
2193
  };
1755
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);
1756
2196
  state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
1757
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);
1758
2201
  state.inType = false;
1759
- state.traverse = (root) => api_traverseAst(root, (node) => {
2202
+ state.traverse = (root) => ast_traverseAst(root, (node) => {
1760
2203
  try {
1761
2204
  if (state.shouldExclude && state.shouldExclude(node)) {
1762
2205
  // don't visit any children, but do call post
@@ -1767,14 +2210,60 @@ function api_collectNamespaces(ast, stateIn) {
1767
2210
  if (state.stack.length != 1) {
1768
2211
  throw new Error("Unexpected stack length for Program node");
1769
2212
  }
2213
+ state.stack[0].node = node;
1770
2214
  break;
1771
2215
  case "TypeSpecList":
1772
2216
  state.inType = true;
1773
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;
1774
2262
  case "BlockStatement": {
1775
2263
  const [parent] = state.stack.slice(-1);
1776
- if (parent.type != "FunctionDeclaration" &&
1777
- parent.type != "BlockStatement") {
2264
+ if (parent.node === node ||
2265
+ (parent.type != "FunctionDeclaration" &&
2266
+ parent.type != "BlockStatement")) {
1778
2267
  break;
1779
2268
  }
1780
2269
  // fall through
@@ -1820,6 +2309,9 @@ function api_collectNamespaces(ast, stateIn) {
1820
2309
  parent.decls[name].push(elm);
1821
2310
  if (node.type == "ModuleDeclaration" ||
1822
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] };
1823
2315
  if (!parent.type_decls)
1824
2316
  parent.type_decls = {};
1825
2317
  if (!api_hasProperty(parent.type_decls, name)) {
@@ -1867,7 +2359,7 @@ function api_collectNamespaces(ast, stateIn) {
1867
2359
  if (!parent.decls)
1868
2360
  parent.decls = {};
1869
2361
  const decls = parent.decls;
1870
- const stack = state.stack.slice();
2362
+ const stack = state.stackClone();
1871
2363
  node.declarations.forEach((decl) => {
1872
2364
  const name = api_variableDeclarationName(decl.id);
1873
2365
  if (!api_hasProperty(decls, name)) {
@@ -1971,8 +2463,14 @@ function api_collectNamespaces(ast, stateIn) {
1971
2463
  state.inType = true;
1972
2464
  break;
1973
2465
  }
1974
- if (state.stack.slice(-1).pop()?.node === node) {
1975
- 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
+ }
1976
2474
  }
1977
2475
  }
1978
2476
  if (ret === false) {
@@ -1993,77 +2491,7 @@ function api_collectNamespaces(ast, stateIn) {
1993
2491
  }
1994
2492
  return state.stack[0];
1995
2493
  }
1996
- function isMCTreeNode(node) {
1997
- return node ? typeof node === "object" && "type" in node : false;
1998
- }
1999
- /*
2000
- * Traverse the ast rooted at node, calling pre before
2001
- * visiting each node, and post after.
2002
- *
2003
- * - if pre returns false, the node is not traversed, and
2004
- * post is not called;
2005
- * - if pre returns a list of child nodes, only those will
2006
- * be traversed
2007
- * - otherwise all child nodes are traversed
2008
- *
2009
- * - if post returns false, the node it was called on is
2010
- * removed.
2011
- */
2012
- function api_traverseAst(node, pre, post) {
2013
- const nodes = pre && pre(node);
2014
- if (nodes === false)
2015
- return;
2016
- for (const key of nodes || Object.keys(node)) {
2017
- const value = node[key];
2018
- if (!value)
2019
- continue;
2020
- if (Array.isArray(value)) {
2021
- const values = value;
2022
- const deletions = values.reduce((state, obj, i) => {
2023
- if (isMCTreeNode(obj)) {
2024
- const repl = api_traverseAst(obj, pre, post);
2025
- if (repl === false) {
2026
- if (!state)
2027
- state = {};
2028
- state[i] = true;
2029
- }
2030
- else if (repl != null) {
2031
- if (!state)
2032
- state = {};
2033
- values[i] = repl;
2034
- }
2035
- }
2036
- return state;
2037
- }, null);
2038
- if (deletions) {
2039
- values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
2040
- }
2041
- }
2042
- else if (isMCTreeNode(value)) {
2043
- const repl = api_traverseAst(value, pre, post);
2044
- if (repl === false) {
2045
- delete node[key];
2046
- }
2047
- else if (repl != null) {
2048
- if (Array.isArray(repl)) {
2049
- throw new Error("Array returned by traverseAst in Node context");
2050
- }
2051
- node[key] = repl;
2052
- }
2053
- }
2054
- }
2055
- return post && post(node);
2056
- }
2057
- function formatAst(node, monkeyCSource = null) {
2058
- if ("comments" in node && !monkeyCSource) {
2059
- // Prettier inserts comments by using the source location to
2060
- // find the original comment, rather than using the contents
2061
- // of the comment as reported by the comment nodes themselves.
2062
- // If all we've got is the ast, rather than the actual
2063
- // source code, this goes horribly wrong, so just drop all
2064
- // the comments.
2065
- delete node.comments;
2066
- }
2494
+ function api_formatAst(node, monkeyCSource = null) {
2067
2495
  /*
2068
2496
  * The estree printer sometimes looks at the parent node without
2069
2497
  * checking that there *is* a parent node (eg it assumes all
@@ -2104,7 +2532,7 @@ function handleException(state, node, exception) {
2104
2532
  .filter((e) => e != null)
2105
2533
  .join(".");
2106
2534
  const location = node.loc && node.loc.source
2107
- ? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
2535
+ ? `${node.loc.source}:${node.loc.start.line || 0}:${node.loc.end.line || 0}`
2108
2536
  : "<unknown>";
2109
2537
  const message = `Got exception \`${exception instanceof Error
2110
2538
  ? exception.message
@@ -2116,9 +2544,65 @@ function handleException(state, node, exception) {
2116
2544
  exception = new Error(message);
2117
2545
  }
2118
2546
  }
2119
- finally {
2547
+ catch (ex) {
2120
2548
  throw exception;
2121
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;
2122
2606
  }
2123
2607
 
2124
2608
  var __webpack_export_target__ = exports;