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