@markw65/monkeyc-optimizer 1.0.21 → 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 +20 -0
- package/build/api.cjs +542 -153
- package/build/optimizer.cjs +310 -126
- package/build/src/api.d.ts +5 -2
- package/build/src/inliner.d.ts +1 -5
- package/build/src/mc-rewrite.d.ts +4 -4
- package/build/src/optimizer.d.ts +25 -8
- package/build/src/pragma-checker.d.ts +1 -1
- package/build/src/util.d.ts +1 -0
- 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"
|
|
@@ -169,11 +172,13 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
169
172
|
case "Identifier":
|
|
170
173
|
case "MemberExpression": {
|
|
171
174
|
const [, results] = state.lookup(arg);
|
|
172
|
-
if (!results ||
|
|
175
|
+
if (!results ||
|
|
176
|
+
results.length !== 1 ||
|
|
177
|
+
results[0].results.length !== 1) {
|
|
173
178
|
safeArgs.push(null);
|
|
174
179
|
return !requireAll;
|
|
175
180
|
}
|
|
176
|
-
const safety = getSafety(results[0]);
|
|
181
|
+
const safety = getSafety(results[0].results[0]);
|
|
177
182
|
safeArgs.push(safety);
|
|
178
183
|
if (!safety) {
|
|
179
184
|
allSafe = false;
|
|
@@ -265,12 +270,6 @@ function inliningLooksUseful(func, node) {
|
|
|
265
270
|
}
|
|
266
271
|
return false;
|
|
267
272
|
}
|
|
268
|
-
var InlineStatus;
|
|
269
|
-
(function (InlineStatus) {
|
|
270
|
-
InlineStatus[InlineStatus["Never"] = 0] = "Never";
|
|
271
|
-
InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
|
|
272
|
-
InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
|
|
273
|
-
})(InlineStatus || (InlineStatus = {}));
|
|
274
273
|
function inlineRequested(state, func) {
|
|
275
274
|
const excludeAnnotations = (func.node.loc?.source &&
|
|
276
275
|
state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
|
|
@@ -280,7 +279,7 @@ function inlineRequested(state, func) {
|
|
|
280
279
|
func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
|
|
281
280
|
(attr.argument.name === "inline" ||
|
|
282
281
|
(attr.argument.name.startsWith("inline_") &&
|
|
283
|
-
hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
|
|
282
|
+
!hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
|
|
284
283
|
return true;
|
|
285
284
|
}
|
|
286
285
|
return false;
|
|
@@ -450,13 +449,13 @@ function inliner_unused(expression, top) {
|
|
|
450
449
|
},
|
|
451
450
|
];
|
|
452
451
|
}
|
|
453
|
-
function
|
|
452
|
+
function inliner_diagnostic(state, loc, message, type = "INFO") {
|
|
454
453
|
if (!loc || !loc.source)
|
|
455
454
|
return;
|
|
456
455
|
const source = loc.source;
|
|
457
456
|
if (!state.diagnostics)
|
|
458
457
|
state.diagnostics = {};
|
|
459
|
-
if (!hasProperty(state.diagnostics, source)) {
|
|
458
|
+
if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
|
|
460
459
|
if (!message)
|
|
461
460
|
return;
|
|
462
461
|
state.diagnostics[source] = [];
|
|
@@ -466,7 +465,7 @@ function diagnostic(state, loc, message) {
|
|
|
466
465
|
if (message) {
|
|
467
466
|
if (index < 0)
|
|
468
467
|
index = diags.length;
|
|
469
|
-
diags[index] = { type
|
|
468
|
+
diags[index] = { type, loc, message };
|
|
470
469
|
}
|
|
471
470
|
else if (index >= 0) {
|
|
472
471
|
diags.splice(index, 1);
|
|
@@ -474,7 +473,7 @@ function diagnostic(state, loc, message) {
|
|
|
474
473
|
}
|
|
475
474
|
function inlineDiagnostic(state, func, call, message) {
|
|
476
475
|
if (inlineRequested(state, func)) {
|
|
477
|
-
|
|
476
|
+
inliner_diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
|
|
478
477
|
}
|
|
479
478
|
}
|
|
480
479
|
function inlineWithArgs(state, func, call, context) {
|
|
@@ -522,7 +521,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
522
521
|
if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
|
|
523
522
|
return null;
|
|
524
523
|
}
|
|
525
|
-
|
|
524
|
+
inliner_diagnostic(state, call.loc, null);
|
|
526
525
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
527
526
|
const last = body.body[body.body.length - 1];
|
|
528
527
|
if (last.type != "ReturnStatement") {
|
|
@@ -585,23 +584,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
585
584
|
// With a bit more work, we could find the guaranteed shortest
|
|
586
585
|
// reference, and then use this to optimize *all* symbols, not
|
|
587
586
|
// just fix inlined ones.
|
|
588
|
-
if (current &&
|
|
589
|
-
current.length === original.length &&
|
|
590
|
-
current.every((item, index) => item == original[index])) {
|
|
587
|
+
if (current && sameLookupResult(original, current)) {
|
|
591
588
|
return lookupNode;
|
|
592
589
|
}
|
|
593
590
|
const node = lookupNode.type === "Identifier"
|
|
594
591
|
? lookupNode
|
|
595
592
|
: lookupNode.property;
|
|
596
|
-
if (original.length === 1 &&
|
|
597
|
-
|
|
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);
|
|
598
597
|
}
|
|
599
|
-
const prefixes = original
|
|
598
|
+
const prefixes = original
|
|
599
|
+
.map((lookupDef) => lookupDef.results.map((sn) => {
|
|
600
600
|
if (isStateNode(sn) && sn.fullName) {
|
|
601
601
|
return sn.fullName;
|
|
602
602
|
}
|
|
603
603
|
return "";
|
|
604
|
-
})
|
|
604
|
+
}))
|
|
605
|
+
.flat();
|
|
605
606
|
if (prefixes.length &&
|
|
606
607
|
prefixes[0].startsWith("$.") &&
|
|
607
608
|
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
@@ -611,9 +612,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
611
612
|
if (found)
|
|
612
613
|
return current;
|
|
613
614
|
const [, results] = state.lookup(current);
|
|
614
|
-
if (results &&
|
|
615
|
-
results.length === original.length &&
|
|
616
|
-
results.every((result, i) => result === original[i])) {
|
|
615
|
+
if (results && sameLookupResult(original, results)) {
|
|
617
616
|
found = true;
|
|
618
617
|
return current;
|
|
619
618
|
}
|
|
@@ -655,6 +654,85 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
655
654
|
|
|
656
655
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
657
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
|
+
|
|
658
736
|
;// CONCATENATED MODULE: ./src/mc-rewrite.ts
|
|
659
737
|
|
|
660
738
|
|
|
@@ -662,43 +740,24 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
|
662
740
|
|
|
663
741
|
|
|
664
742
|
|
|
665
|
-
|
|
666
|
-
allImports.forEach(({ node, stack }) => {
|
|
667
|
-
const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
|
|
668
|
-
if (name && module) {
|
|
669
|
-
const [parent] = stack.slice(-1);
|
|
670
|
-
if (!parent.decls)
|
|
671
|
-
parent.decls = {};
|
|
672
|
-
const decls = parent.decls;
|
|
673
|
-
if (!hasProperty(decls, name))
|
|
674
|
-
decls[name] = [];
|
|
675
|
-
module.forEach((m) => {
|
|
676
|
-
if (isStateNode(m) && m.type == "ModuleDeclaration") {
|
|
677
|
-
pushUnique(decls[name], m);
|
|
678
|
-
if (!parent.type_decls)
|
|
679
|
-
parent.type_decls = {};
|
|
680
|
-
const tdecls = parent.type_decls;
|
|
681
|
-
if (!hasProperty(tdecls, name))
|
|
682
|
-
tdecls[name] = [];
|
|
683
|
-
pushUnique(tdecls[name], m);
|
|
684
|
-
if (node.type == "ImportModule" && m.type_decls) {
|
|
685
|
-
Object.entries(m.type_decls).forEach(([name, decls]) => {
|
|
686
|
-
if (!hasProperty(tdecls, name))
|
|
687
|
-
tdecls[name] = [];
|
|
688
|
-
decls.forEach((decl) => pushUnique(tdecls[name], decl));
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
}
|
|
743
|
+
|
|
696
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"];
|
|
697
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
|
+
}
|
|
698
754
|
if (elm.node.superClass) {
|
|
699
|
-
const [name,
|
|
700
|
-
const superClass =
|
|
701
|
-
|
|
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");
|
|
702
761
|
// set it "true" if there is a superClass, but we can't find it.
|
|
703
762
|
elm.superClass = superClass && superClass.length ? superClass : true;
|
|
704
763
|
if (name && elm.superClass !== true) {
|
|
@@ -730,6 +789,9 @@ function collectClassInfo(state) {
|
|
|
730
789
|
elm.decls[name] = elm.superClass;
|
|
731
790
|
}
|
|
732
791
|
}
|
|
792
|
+
else if (elm !== object[0]) {
|
|
793
|
+
elm.superClass = object;
|
|
794
|
+
}
|
|
733
795
|
});
|
|
734
796
|
const markOverrides = (cls, scls) => {
|
|
735
797
|
if (scls === true)
|
|
@@ -783,11 +845,12 @@ function getFileASTs(fnMap) {
|
|
|
783
845
|
return ok;
|
|
784
846
|
}, true));
|
|
785
847
|
}
|
|
786
|
-
async function analyze(fnMap) {
|
|
848
|
+
async function analyze(fnMap, barrelList, config) {
|
|
787
849
|
let hasTests = false;
|
|
788
|
-
|
|
850
|
+
let markApi = true;
|
|
789
851
|
const preState = {
|
|
790
852
|
fnMap,
|
|
853
|
+
config,
|
|
791
854
|
allFunctions: [],
|
|
792
855
|
allClasses: [],
|
|
793
856
|
shouldExclude(node) {
|
|
@@ -815,45 +878,37 @@ async function analyze(fnMap) {
|
|
|
815
878
|
}
|
|
816
879
|
return false;
|
|
817
880
|
},
|
|
818
|
-
|
|
881
|
+
pre(node, state) {
|
|
819
882
|
switch (node.type) {
|
|
820
883
|
case "FunctionDeclaration":
|
|
884
|
+
if (markApi) {
|
|
885
|
+
node.body = null;
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
case "ModuleDeclaration":
|
|
821
889
|
case "ClassDeclaration": {
|
|
822
890
|
const [scope] = state.stack.slice(-1);
|
|
823
|
-
|
|
824
|
-
scope.stack = stack;
|
|
891
|
+
scope.stack = state.stackClone().slice(0, -1);
|
|
825
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"));
|
|
826
898
|
state.allFunctions.push(scope);
|
|
827
899
|
}
|
|
828
|
-
else {
|
|
900
|
+
else if (scope.type === "ClassDeclaration") {
|
|
829
901
|
state.allClasses.push(scope);
|
|
830
902
|
}
|
|
831
|
-
|
|
903
|
+
break;
|
|
832
904
|
}
|
|
833
|
-
case "Using":
|
|
834
|
-
case "ImportModule":
|
|
835
|
-
allImports.push({ node, stack: state.stack.slice() });
|
|
836
|
-
return null;
|
|
837
|
-
default:
|
|
838
|
-
return null;
|
|
839
905
|
}
|
|
906
|
+
return null;
|
|
840
907
|
},
|
|
841
908
|
};
|
|
842
|
-
await getApiMapping(preState);
|
|
909
|
+
await getApiMapping(preState, barrelList);
|
|
910
|
+
markApi = false;
|
|
843
911
|
const state = preState;
|
|
844
|
-
// Mark all functions from api.mir as "special" by
|
|
845
|
-
// setting their bodies to null. In api.mir, they're
|
|
846
|
-
// all empty, which makes it look like they're
|
|
847
|
-
// do-nothing functions.
|
|
848
|
-
const markApi = (node) => {
|
|
849
|
-
if (node.type == "FunctionDeclaration") {
|
|
850
|
-
node.node.body = null;
|
|
851
|
-
}
|
|
852
|
-
if (isStateNode(node) && node.decls) {
|
|
853
|
-
Object.values(node.decls).forEach((v) => v.forEach(markApi));
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
markApi(state.stack[0]);
|
|
857
912
|
await getFileASTs(fnMap);
|
|
858
913
|
Object.entries(fnMap).forEach(([name, value]) => {
|
|
859
914
|
const { ast, parserError } = value;
|
|
@@ -866,8 +921,28 @@ async function analyze(fnMap) {
|
|
|
866
921
|
});
|
|
867
922
|
delete state.shouldExclude;
|
|
868
923
|
delete state.post;
|
|
869
|
-
processImports(allImports, state.lookup);
|
|
870
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
|
+
}
|
|
871
946
|
return state;
|
|
872
947
|
}
|
|
873
948
|
function compareLiteralLike(a, b) {
|
|
@@ -877,11 +952,11 @@ function compareLiteralLike(a, b) {
|
|
|
877
952
|
b = b.left;
|
|
878
953
|
return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
|
|
879
954
|
}
|
|
880
|
-
function getLiteralFromDecls(
|
|
881
|
-
if (!
|
|
955
|
+
function getLiteralFromDecls(lookupDefns) {
|
|
956
|
+
if (!lookupDefns.length)
|
|
882
957
|
return null;
|
|
883
958
|
let result = null;
|
|
884
|
-
if (
|
|
959
|
+
if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
|
|
885
960
|
if (d.type === "EnumStringMember" ||
|
|
886
961
|
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
887
962
|
const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
|
|
@@ -896,7 +971,7 @@ function getLiteralFromDecls(decls) {
|
|
|
896
971
|
}
|
|
897
972
|
}
|
|
898
973
|
return false;
|
|
899
|
-
})) {
|
|
974
|
+
}))) {
|
|
900
975
|
return result;
|
|
901
976
|
}
|
|
902
977
|
return null;
|
|
@@ -1107,20 +1182,23 @@ function markFunctionCalled(state, func) {
|
|
|
1107
1182
|
}
|
|
1108
1183
|
pushUnique(state.calledFunctions[func.id.name], func);
|
|
1109
1184
|
}
|
|
1110
|
-
async function optimizeMonkeyC(fnMap) {
|
|
1185
|
+
async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
1111
1186
|
const state = {
|
|
1112
|
-
...(await analyze(fnMap)),
|
|
1187
|
+
...(await analyze(fnMap, barrelList, config)),
|
|
1113
1188
|
localsStack: [{}],
|
|
1114
1189
|
exposed: {},
|
|
1115
1190
|
calledFunctions: {},
|
|
1116
1191
|
};
|
|
1117
|
-
const replace = (node) => {
|
|
1192
|
+
const replace = (node, old) => {
|
|
1118
1193
|
if (node === false || node === null)
|
|
1119
1194
|
return node;
|
|
1120
1195
|
const rep = state.traverse(node);
|
|
1121
|
-
|
|
1196
|
+
if (rep === false || Array.isArray(rep))
|
|
1197
|
+
return rep;
|
|
1198
|
+
return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
|
|
1122
1199
|
};
|
|
1123
1200
|
const inPlaceReplacement = (node, obj) => {
|
|
1201
|
+
const { start, end, loc } = node;
|
|
1124
1202
|
for (const k of Object.keys(node)) {
|
|
1125
1203
|
delete node[k];
|
|
1126
1204
|
}
|
|
@@ -1128,13 +1206,16 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1128
1206
|
obj = {
|
|
1129
1207
|
type: "BinaryExpression",
|
|
1130
1208
|
operator: "as",
|
|
1131
|
-
left: obj,
|
|
1209
|
+
left: { ...obj, start, end, loc },
|
|
1132
1210
|
right: { type: "TypeSpecList", ts: [obj.enumType] },
|
|
1133
1211
|
};
|
|
1134
1212
|
}
|
|
1135
1213
|
for (const [k, v] of Object.entries(obj)) {
|
|
1136
1214
|
node[k] = v;
|
|
1137
1215
|
}
|
|
1216
|
+
node.loc = loc;
|
|
1217
|
+
node.start = start;
|
|
1218
|
+
node.end = end;
|
|
1138
1219
|
};
|
|
1139
1220
|
const lookupAndReplace = (node) => {
|
|
1140
1221
|
const [, objects] = state.lookup(node);
|
|
@@ -1253,6 +1334,37 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1253
1334
|
}
|
|
1254
1335
|
return ["init"];
|
|
1255
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;
|
|
1256
1368
|
case "UnaryExpression":
|
|
1257
1369
|
if (node.operator == ":") {
|
|
1258
1370
|
// If we produce a Symbol, for a given name,
|
|
@@ -1335,7 +1447,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1335
1447
|
}
|
|
1336
1448
|
const opt = optimizeNode(node);
|
|
1337
1449
|
if (opt) {
|
|
1338
|
-
return replace(opt);
|
|
1450
|
+
return replace(opt, node);
|
|
1339
1451
|
}
|
|
1340
1452
|
switch (node.type) {
|
|
1341
1453
|
case "ConditionalExpression":
|
|
@@ -1345,7 +1457,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1345
1457
|
const rep = node.test.value ? node.consequent : node.alternate;
|
|
1346
1458
|
if (!rep)
|
|
1347
1459
|
return false;
|
|
1348
|
-
return replace(rep);
|
|
1460
|
+
return replace(rep, rep);
|
|
1349
1461
|
}
|
|
1350
1462
|
break;
|
|
1351
1463
|
case "WhileStatement":
|
|
@@ -1360,11 +1472,11 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1360
1472
|
break;
|
|
1361
1473
|
case "ReturnStatement":
|
|
1362
1474
|
if (node.argument && node.argument.type === "CallExpression") {
|
|
1363
|
-
return replace(optimizeCall(state, node.argument, node));
|
|
1475
|
+
return replace(optimizeCall(state, node.argument, node), node.argument);
|
|
1364
1476
|
}
|
|
1365
1477
|
break;
|
|
1366
1478
|
case "CallExpression": {
|
|
1367
|
-
return replace(optimizeCall(state, node, null));
|
|
1479
|
+
return replace(optimizeCall(state, node, null), node);
|
|
1368
1480
|
}
|
|
1369
1481
|
case "AssignmentExpression":
|
|
1370
1482
|
if (node.operator === "=" &&
|
|
@@ -1376,7 +1488,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1376
1488
|
break;
|
|
1377
1489
|
case "ExpressionStatement":
|
|
1378
1490
|
if (node.expression.type === "CallExpression") {
|
|
1379
|
-
return replace(optimizeCall(state, node.expression, node));
|
|
1491
|
+
return replace(optimizeCall(state, node.expression, node), node.expression);
|
|
1380
1492
|
}
|
|
1381
1493
|
else if (node.expression.type === "AssignmentExpression") {
|
|
1382
1494
|
if (node.expression.right.type === "CallExpression") {
|
|
@@ -1388,10 +1500,10 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1388
1500
|
}
|
|
1389
1501
|
if (!ok && node.expression.operator == "=") {
|
|
1390
1502
|
const [, result] = state.lookup(node.expression.left);
|
|
1391
|
-
ok = result
|
|
1503
|
+
ok = !!result;
|
|
1392
1504
|
}
|
|
1393
1505
|
if (ok) {
|
|
1394
|
-
return replace(optimizeCall(state, node.expression.right, node.expression));
|
|
1506
|
+
return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
|
|
1395
1507
|
}
|
|
1396
1508
|
}
|
|
1397
1509
|
}
|
|
@@ -1399,7 +1511,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1399
1511
|
const ret = unused(node.expression, true);
|
|
1400
1512
|
if (ret) {
|
|
1401
1513
|
return ret
|
|
1402
|
-
.map(replace)
|
|
1514
|
+
.map((r) => replace(r, r))
|
|
1403
1515
|
.flat(1)
|
|
1404
1516
|
.filter((s) => !!s);
|
|
1405
1517
|
}
|
|
@@ -1420,6 +1532,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1420
1532
|
delete state.post;
|
|
1421
1533
|
const cleanup = (node) => {
|
|
1422
1534
|
switch (node.type) {
|
|
1535
|
+
case "ThisExpression":
|
|
1536
|
+
node.text = "self";
|
|
1537
|
+
break;
|
|
1423
1538
|
case "EnumStringBody":
|
|
1424
1539
|
if (node.members.every((m) => {
|
|
1425
1540
|
const name = "name" in m ? m.name : m.id.name;
|
|
@@ -1484,6 +1599,19 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1484
1599
|
return false;
|
|
1485
1600
|
}
|
|
1486
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
|
+
}
|
|
1487
1615
|
}
|
|
1488
1616
|
return null;
|
|
1489
1617
|
};
|
|
@@ -1499,9 +1627,12 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1499
1627
|
return state.diagnostics;
|
|
1500
1628
|
}
|
|
1501
1629
|
function optimizeCall(state, node, context) {
|
|
1502
|
-
const [name, results] = state.
|
|
1630
|
+
const [name, results] = state.lookupNonlocal(node.callee);
|
|
1503
1631
|
const callees = results &&
|
|
1504
|
-
results
|
|
1632
|
+
results
|
|
1633
|
+
.map((r) => r.results)
|
|
1634
|
+
.flat()
|
|
1635
|
+
.filter((c) => c.type === "FunctionDeclaration");
|
|
1505
1636
|
if (!callees || !callees.length) {
|
|
1506
1637
|
const n = name ||
|
|
1507
1638
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -1606,6 +1737,8 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
|
|
|
1606
1737
|
|
|
1607
1738
|
|
|
1608
1739
|
|
|
1740
|
+
|
|
1741
|
+
|
|
1609
1742
|
/*
|
|
1610
1743
|
* This is an unfortunate hack. I want to be able to extract things
|
|
1611
1744
|
* like the types of all of a Class's variables (in particular the type
|
|
@@ -1616,16 +1749,30 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
|
|
|
1616
1749
|
* but those are at least in a standard format.
|
|
1617
1750
|
*/
|
|
1618
1751
|
// Extract all enum values from api.mir
|
|
1619
|
-
async function api_getApiMapping(state) {
|
|
1752
|
+
async function api_getApiMapping(state, barrelList) {
|
|
1620
1753
|
// get the path to the currently active sdk
|
|
1621
1754
|
const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
|
|
1622
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("")}}`;
|
|
1623
1766
|
const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
|
|
1624
1767
|
.toString()
|
|
1625
1768
|
.replace(/\r\n/g, "\n")
|
|
1626
1769
|
.replace(/^\s*\[.*?\]\s*$/gm, "")
|
|
1627
1770
|
//.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
|
|
1628
|
-
.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("");
|
|
1629
1776
|
try {
|
|
1630
1777
|
const result = api_collectNamespaces(parser.parse(api, null, {
|
|
1631
1778
|
filepath: "api.mir",
|
|
@@ -1643,19 +1790,19 @@ async function api_getApiMapping(state) {
|
|
|
1643
1790
|
(value.type !== "VariableDeclarator" || value.kind != "const")) {
|
|
1644
1791
|
throw `Negative constant ${fixup} did not refer to a constant`;
|
|
1645
1792
|
}
|
|
1646
|
-
const init = value.init;
|
|
1793
|
+
const init = getLiteralNode(value.init);
|
|
1647
1794
|
if (!init || init.type !== "Literal") {
|
|
1648
1795
|
throw `Negative constant ${fixup} was not a Literal`;
|
|
1649
1796
|
}
|
|
1650
1797
|
if (typeof init.value !== "number") {
|
|
1651
|
-
console.log(`Negative fixup ${fixup} was
|
|
1798
|
+
console.log(`Negative fixup ${fixup} was not a number!`);
|
|
1652
1799
|
}
|
|
1653
1800
|
else if (init.value > 0) {
|
|
1654
1801
|
init.value = -init.value;
|
|
1655
1802
|
init.raw = "-" + init.raw;
|
|
1656
1803
|
}
|
|
1657
1804
|
else {
|
|
1658
|
-
console.log(`Negative fixup ${fixup} was already negative!`);
|
|
1805
|
+
// console.log(`Negative fixup ${fixup} was already negative!`);
|
|
1659
1806
|
}
|
|
1660
1807
|
});
|
|
1661
1808
|
return result;
|
|
@@ -1674,67 +1821,195 @@ function api_isStateNode(node) {
|
|
|
1674
1821
|
function api_variableDeclarationName(node) {
|
|
1675
1822
|
return ("left" in node ? node.left : node).name;
|
|
1676
1823
|
}
|
|
1677
|
-
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
|
+
};
|
|
1678
1861
|
if (api_isStateNode(ns)) {
|
|
1679
|
-
if (api_hasProperty(ns[decls], name)) {
|
|
1680
|
-
return ns[decls][name];
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
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;
|
|
1690
1873
|
}
|
|
1691
1874
|
}
|
|
1692
1875
|
return null;
|
|
1693
1876
|
}
|
|
1694
|
-
function
|
|
1695
|
-
|
|
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;
|
|
1696
1914
|
switch (node.type) {
|
|
1697
1915
|
case "MemberExpression": {
|
|
1698
1916
|
if (node.property.type != "Identifier" || node.computed)
|
|
1699
1917
|
break;
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
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];
|
|
1709
1954
|
}
|
|
1710
1955
|
}
|
|
1711
|
-
|
|
1956
|
+
if (!result)
|
|
1957
|
+
return [null, null];
|
|
1958
|
+
return [name || property.name, result];
|
|
1712
1959
|
}
|
|
1713
1960
|
case "ThisExpression": {
|
|
1714
|
-
for (let i = stack.length
|
|
1961
|
+
for (let i = stack.length;;) {
|
|
1715
1962
|
const si = stack[i];
|
|
1716
1963
|
if (si.type == "ModuleDeclaration" ||
|
|
1717
1964
|
si.type == "ClassDeclaration" ||
|
|
1718
1965
|
!i) {
|
|
1719
|
-
return [
|
|
1966
|
+
return [
|
|
1967
|
+
name || si.name,
|
|
1968
|
+
[{ parent: i ? stack[i - 1] : null, results: [si] }],
|
|
1969
|
+
];
|
|
1720
1970
|
}
|
|
1721
1971
|
}
|
|
1722
|
-
break;
|
|
1723
1972
|
}
|
|
1724
1973
|
case "Identifier": {
|
|
1725
1974
|
if (node.name == "$") {
|
|
1726
|
-
return [name || node.name, [stack[0]]
|
|
1975
|
+
return [name || node.name, [{ parent: null, results: [stack[0]] }]];
|
|
1727
1976
|
}
|
|
1977
|
+
let inStatic = false;
|
|
1978
|
+
let checkedImports = false;
|
|
1728
1979
|
for (let i = stack.length; i--;) {
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
|
|
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;
|
|
1732
2007
|
}
|
|
1733
2008
|
}
|
|
1734
|
-
|
|
2009
|
+
return [null, null];
|
|
1735
2010
|
}
|
|
1736
2011
|
}
|
|
1737
|
-
return [
|
|
2012
|
+
return [false, false];
|
|
1738
2013
|
}
|
|
1739
2014
|
function api_collectNamespaces(ast, stateIn) {
|
|
1740
2015
|
const state = (stateIn || {});
|
|
@@ -1766,8 +2041,12 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1766
2041
|
}
|
|
1767
2042
|
};
|
|
1768
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);
|
|
1769
2045
|
state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
|
|
1770
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);
|
|
1771
2050
|
state.inType = false;
|
|
1772
2051
|
state.traverse = (root) => api_traverseAst(root, (node) => {
|
|
1773
2052
|
try {
|
|
@@ -1780,14 +2059,60 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1780
2059
|
if (state.stack.length != 1) {
|
|
1781
2060
|
throw new Error("Unexpected stack length for Program node");
|
|
1782
2061
|
}
|
|
2062
|
+
state.stack[0].node = node;
|
|
1783
2063
|
break;
|
|
1784
2064
|
case "TypeSpecList":
|
|
1785
2065
|
state.inType = true;
|
|
1786
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;
|
|
1787
2111
|
case "BlockStatement": {
|
|
1788
2112
|
const [parent] = state.stack.slice(-1);
|
|
1789
|
-
if (parent.
|
|
1790
|
-
parent.type != "
|
|
2113
|
+
if (parent.node === node ||
|
|
2114
|
+
(parent.type != "FunctionDeclaration" &&
|
|
2115
|
+
parent.type != "BlockStatement")) {
|
|
1791
2116
|
break;
|
|
1792
2117
|
}
|
|
1793
2118
|
// fall through
|
|
@@ -1833,6 +2158,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1833
2158
|
parent.decls[name].push(elm);
|
|
1834
2159
|
if (node.type == "ModuleDeclaration" ||
|
|
1835
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] };
|
|
1836
2164
|
if (!parent.type_decls)
|
|
1837
2165
|
parent.type_decls = {};
|
|
1838
2166
|
if (!api_hasProperty(parent.type_decls, name)) {
|
|
@@ -1880,7 +2208,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1880
2208
|
if (!parent.decls)
|
|
1881
2209
|
parent.decls = {};
|
|
1882
2210
|
const decls = parent.decls;
|
|
1883
|
-
const stack = state.
|
|
2211
|
+
const stack = state.stackClone();
|
|
1884
2212
|
node.declarations.forEach((decl) => {
|
|
1885
2213
|
const name = api_variableDeclarationName(decl.id);
|
|
1886
2214
|
if (!api_hasProperty(decls, name)) {
|
|
@@ -1984,8 +2312,14 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1984
2312
|
state.inType = true;
|
|
1985
2313
|
break;
|
|
1986
2314
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
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
|
+
}
|
|
1989
2323
|
}
|
|
1990
2324
|
}
|
|
1991
2325
|
if (ret === false) {
|
|
@@ -2067,7 +2401,7 @@ function api_traverseAst(node, pre, post) {
|
|
|
2067
2401
|
}
|
|
2068
2402
|
return post && post(node);
|
|
2069
2403
|
}
|
|
2070
|
-
function
|
|
2404
|
+
function api_formatAst(node, monkeyCSource = null) {
|
|
2071
2405
|
/*
|
|
2072
2406
|
* The estree printer sometimes looks at the parent node without
|
|
2073
2407
|
* checking that there *is* a parent node (eg it assumes all
|
|
@@ -2108,7 +2442,7 @@ function handleException(state, node, exception) {
|
|
|
2108
2442
|
.filter((e) => e != null)
|
|
2109
2443
|
.join(".");
|
|
2110
2444
|
const location = node.loc && node.loc.source
|
|
2111
|
-
? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
|
|
2445
|
+
? `${node.loc.source}:${node.loc.start.line || 0}:${node.loc.end.line || 0}`
|
|
2112
2446
|
: "<unknown>";
|
|
2113
2447
|
const message = `Got exception \`${exception instanceof Error
|
|
2114
2448
|
? exception.message
|
|
@@ -2124,6 +2458,61 @@ function handleException(state, node, exception) {
|
|
|
2124
2458
|
throw exception;
|
|
2125
2459
|
}
|
|
2126
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
|
+
}
|
|
2127
2516
|
|
|
2128
2517
|
var __webpack_export_target__ = exports;
|
|
2129
2518
|
for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];
|