@markw65/monkeyc-optimizer 1.0.30 → 1.0.33
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 +50 -0
- package/build/api.cjs +1012 -299
- package/build/optimizer.cjs +909 -386
- package/build/src/api.d.ts +5 -1
- package/build/src/ast.d.ts +21 -1
- package/build/src/build.d.ts +1 -0
- package/build/src/control-flow.d.ts +1 -0
- package/build/src/function-info.d.ts +12 -0
- package/build/src/inliner.d.ts +1 -0
- package/build/src/jungles.d.ts +1 -0
- package/build/src/mc-rewrite.d.ts +2 -1
- package/build/src/optimizer-types.d.ts +188 -0
- package/build/src/optimizer.d.ts +4 -174
- package/build/src/pragma-checker.d.ts +2 -1
- package/build/src/pre.d.ts +1 -0
- package/build/src/unused-exprs.d.ts +3 -0
- package/build/src/variable-renamer.d.ts +1 -0
- package/build/src/visitor.d.ts +1 -0
- package/package.json +6 -6
package/build/api.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiMapping,hasProperty,isStateNode,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
|
|
1
|
+
0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
|
|
2
2
|
/******/ (() => { // webpackBootstrap
|
|
3
3
|
/******/ var __webpack_modules__ = ({
|
|
4
4
|
|
|
@@ -261,9 +261,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
261
261
|
"collectNamespaces": () => (/* binding */ api_collectNamespaces),
|
|
262
262
|
"findUsingForNode": () => (/* binding */ findUsingForNode),
|
|
263
263
|
"formatAst": () => (/* binding */ api_formatAst),
|
|
264
|
+
"getApiFunctionInfo": () => (/* binding */ api_getApiFunctionInfo),
|
|
264
265
|
"getApiMapping": () => (/* binding */ api_getApiMapping),
|
|
265
266
|
"hasProperty": () => (/* reexport */ ast_hasProperty),
|
|
267
|
+
"isLookupCandidate": () => (/* binding */ api_isLookupCandidate),
|
|
266
268
|
"isStateNode": () => (/* binding */ api_isStateNode),
|
|
269
|
+
"markInvokeClassMethod": () => (/* binding */ api_markInvokeClassMethod),
|
|
267
270
|
"sameLookupResult": () => (/* binding */ api_sameLookupResult),
|
|
268
271
|
"traverseAst": () => (/* reexport */ ast_traverseAst),
|
|
269
272
|
"variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
|
|
@@ -278,6 +281,7 @@ const promises_namespaceObject = require("fs/promises");
|
|
|
278
281
|
;// CONCATENATED MODULE: external "prettier"
|
|
279
282
|
const external_prettier_namespaceObject = require("prettier");
|
|
280
283
|
;// CONCATENATED MODULE: ./src/ast.ts
|
|
284
|
+
|
|
281
285
|
/*
|
|
282
286
|
* This ensures that mctreeTypeInfo has every key of MCTreeTypeInfo,
|
|
283
287
|
* and that the corresponding arrays contain every element of the
|
|
@@ -464,21 +468,188 @@ function ast_withLoc(node, start, end) {
|
|
|
464
468
|
}
|
|
465
469
|
return node;
|
|
466
470
|
}
|
|
467
|
-
function ast_withLocDeep(node, start, end) {
|
|
468
|
-
node = ast_withLoc({ ...node }, start, end);
|
|
471
|
+
function ast_withLocDeep(node, start, end, inplace) {
|
|
472
|
+
node = ast_withLoc(inplace ? node : { ...node }, start, end);
|
|
469
473
|
for (const key of mctreeTypeInfo[node.type].keys) {
|
|
470
474
|
const value = node[key];
|
|
471
475
|
if (!value)
|
|
472
476
|
continue;
|
|
473
|
-
const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end) : v;
|
|
477
|
+
const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end, inplace) : v;
|
|
474
478
|
const repl = Array.isArray(value) ? value.map(fix) : fix(value);
|
|
475
|
-
node[key] = repl;
|
|
479
|
+
inplace || (node[key] = repl);
|
|
476
480
|
}
|
|
477
481
|
return node;
|
|
478
482
|
}
|
|
483
|
+
function ast_cloneDeep(node) {
|
|
484
|
+
return ast_withLocDeep(node, null);
|
|
485
|
+
}
|
|
486
|
+
function ast_getNodeValue(node) {
|
|
487
|
+
if (node.type == "BinaryExpression" &&
|
|
488
|
+
node.operator == "as" &&
|
|
489
|
+
node.right.type == "TypeSpecList" &&
|
|
490
|
+
node.right.ts.length == 1 &&
|
|
491
|
+
typeof node.right.ts[0] == "string") {
|
|
492
|
+
// this is a cast we inserted to retain the type of an enum
|
|
493
|
+
// any arithmetic on it will revert to "Number", or "Long",
|
|
494
|
+
// so just ignore it.
|
|
495
|
+
return ast_getNodeValue(node.left);
|
|
496
|
+
}
|
|
497
|
+
if (node.type != "Literal") {
|
|
498
|
+
return [null, null];
|
|
499
|
+
}
|
|
500
|
+
if (node.value === null) {
|
|
501
|
+
return [node, "Null"];
|
|
502
|
+
}
|
|
503
|
+
const type = typeof node.value;
|
|
504
|
+
if (type === "number") {
|
|
505
|
+
const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
|
|
506
|
+
if (match) {
|
|
507
|
+
return match[2] === "l" || match[2] === "L"
|
|
508
|
+
? [node, "Long"]
|
|
509
|
+
: [node, "Number"];
|
|
510
|
+
}
|
|
511
|
+
return [node, node.raw.endsWith("d") ? "Double" : "Float"];
|
|
512
|
+
}
|
|
513
|
+
if (type === "bigint") {
|
|
514
|
+
return [node, "Long"];
|
|
515
|
+
}
|
|
516
|
+
if (type === "string") {
|
|
517
|
+
return [node, "String"];
|
|
518
|
+
}
|
|
519
|
+
if (type === "boolean") {
|
|
520
|
+
return [node, "Boolean"];
|
|
521
|
+
}
|
|
522
|
+
throw new Error(`Literal has unknown type '${type}'`);
|
|
523
|
+
}
|
|
479
524
|
|
|
480
525
|
;// CONCATENATED MODULE: external "./api.cjs"
|
|
481
526
|
const external_api_cjs_namespaceObject = require("./api.cjs");
|
|
527
|
+
;// CONCATENATED MODULE: ./src/function-info.ts
|
|
528
|
+
|
|
529
|
+
function function_info_cloneSet(ae) {
|
|
530
|
+
return new Set(ae);
|
|
531
|
+
}
|
|
532
|
+
function function_info_mergeSet(a, b) {
|
|
533
|
+
b.forEach((event) => a.add(event));
|
|
534
|
+
}
|
|
535
|
+
function recordModifiedDecl(func, decl) {
|
|
536
|
+
if (!func.next_info) {
|
|
537
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
538
|
+
}
|
|
539
|
+
func.next_info.modifiedDecls.add(decl);
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
function function_info_recordModifiedDecls(func, lookupDefs) {
|
|
543
|
+
lookupDefs.forEach((lookupDef) => lookupDef.results.forEach((result) => {
|
|
544
|
+
if (result.type == "VariableDeclarator" && result.node.kind === "var") {
|
|
545
|
+
recordModifiedDecl(func, result);
|
|
546
|
+
}
|
|
547
|
+
}));
|
|
548
|
+
}
|
|
549
|
+
function function_info_recordModifiedName(func, name) {
|
|
550
|
+
if (!func.next_info) {
|
|
551
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
552
|
+
}
|
|
553
|
+
if (!func.next_info.modifiedNames) {
|
|
554
|
+
func.next_info.modifiedNames = new Set();
|
|
555
|
+
}
|
|
556
|
+
func.next_info.modifiedNames.add(name);
|
|
557
|
+
}
|
|
558
|
+
function function_info_recordModifiedUnknown(func) {
|
|
559
|
+
if (!func.next_info) {
|
|
560
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
561
|
+
}
|
|
562
|
+
func.next_info.modifiedUnknown = true;
|
|
563
|
+
}
|
|
564
|
+
function recordCalledFunc(func, callee) {
|
|
565
|
+
if (!func.next_info) {
|
|
566
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
567
|
+
}
|
|
568
|
+
func.next_info.calledFuncs.add(callee);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
function function_info_recordCalledFuncs(func, callees) {
|
|
572
|
+
callees.forEach((callee) => {
|
|
573
|
+
recordCalledFunc(func, callee);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function function_info_functionMayModify(state, func, decl) {
|
|
577
|
+
const info = func.info;
|
|
578
|
+
if (!info || info.modifiedUnknown)
|
|
579
|
+
return true;
|
|
580
|
+
if (info.resolvedDecls) {
|
|
581
|
+
return info.resolvedDecls.has(decl);
|
|
582
|
+
}
|
|
583
|
+
if (info.modifiedNames?.has(decl.name))
|
|
584
|
+
return true;
|
|
585
|
+
if (info.modifiedDecls.has(decl))
|
|
586
|
+
return true;
|
|
587
|
+
const visited = new Set();
|
|
588
|
+
const resolved = new Set();
|
|
589
|
+
const resolveDecls = (f) => {
|
|
590
|
+
if (visited.has(f))
|
|
591
|
+
return true;
|
|
592
|
+
if (!f.info)
|
|
593
|
+
return false;
|
|
594
|
+
if (f.info.modifiedUnknown) {
|
|
595
|
+
info.modifiedUnknown = true;
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
if (f.info.modifiedNames) {
|
|
599
|
+
if (info.modifiedNames) {
|
|
600
|
+
function_info_mergeSet(info.modifiedNames, f.info.modifiedNames);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
info.modifiedNames = function_info_cloneSet(f.info.modifiedNames);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function_info_mergeSet(resolved, f.info.modifiedDecls);
|
|
607
|
+
visited.add(f);
|
|
608
|
+
const q = true;
|
|
609
|
+
if (q &&
|
|
610
|
+
f.info.callsExposed &&
|
|
611
|
+
state.exposed &&
|
|
612
|
+
!Object.keys(state.exposed).every((key) => !state.allFunctions[key] ||
|
|
613
|
+
state.allFunctions[key].every(resolveDecls))) {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
return Array.from(f.info.calledFuncs).every(resolveDecls);
|
|
617
|
+
};
|
|
618
|
+
if (resolveDecls(func)) {
|
|
619
|
+
info.resolvedDecls = resolved;
|
|
620
|
+
return resolved.has(decl);
|
|
621
|
+
}
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
function function_info_findCallees(lookupDefs) {
|
|
625
|
+
const decls = lookupDefs.reduce((decls, r) => (decls ? decls.concat(r.results) : r.results), null);
|
|
626
|
+
return (decls &&
|
|
627
|
+
decls.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
|
|
628
|
+
}
|
|
629
|
+
function function_info_findCalleesForNew(lookupDefs) {
|
|
630
|
+
const initializer = (decl) => {
|
|
631
|
+
if (hasProperty(decl.decls, "initialize")) {
|
|
632
|
+
return decl.decls["initialize"];
|
|
633
|
+
}
|
|
634
|
+
if (decl.superClass && decl.superClass !== true) {
|
|
635
|
+
return decl.superClass.reduce((cur, cls) => {
|
|
636
|
+
const init = initializer(cls);
|
|
637
|
+
if (init) {
|
|
638
|
+
if (!cur)
|
|
639
|
+
return init;
|
|
640
|
+
return cur.concat(init);
|
|
641
|
+
}
|
|
642
|
+
return cur;
|
|
643
|
+
}, null);
|
|
644
|
+
}
|
|
645
|
+
return null;
|
|
646
|
+
};
|
|
647
|
+
return lookupDefs.flatMap((r) => r.results
|
|
648
|
+
.filter((decl) => decl.type === "ClassDeclaration")
|
|
649
|
+
.flatMap(initializer)
|
|
650
|
+
.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
|
|
651
|
+
}
|
|
652
|
+
|
|
482
653
|
;// CONCATENATED MODULE: ./src/variable-renamer.ts
|
|
483
654
|
|
|
484
655
|
|
|
@@ -539,6 +710,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
|
|
|
539
710
|
|
|
540
711
|
|
|
541
712
|
|
|
713
|
+
|
|
542
714
|
function getArgSafety(state, func, args, requireAll) {
|
|
543
715
|
// determine whether decl might be changed by a function call
|
|
544
716
|
// or assignment during the evaluation of FunctionStateNode.
|
|
@@ -571,8 +743,9 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
571
743
|
}
|
|
572
744
|
};
|
|
573
745
|
const safeArgs = [];
|
|
746
|
+
const argDecls = [];
|
|
574
747
|
let allSafe = true;
|
|
575
|
-
if (!args.every((arg) => {
|
|
748
|
+
if (!args.every((arg, i) => {
|
|
576
749
|
switch (arg.type) {
|
|
577
750
|
case "Literal":
|
|
578
751
|
safeArgs.push(true);
|
|
@@ -586,13 +759,17 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
586
759
|
safeArgs.push(null);
|
|
587
760
|
return !requireAll;
|
|
588
761
|
}
|
|
589
|
-
const
|
|
762
|
+
const decl = results[0].results[0];
|
|
763
|
+
const safety = getSafety(decl);
|
|
590
764
|
safeArgs.push(safety);
|
|
591
765
|
if (!safety) {
|
|
592
766
|
allSafe = false;
|
|
593
767
|
if (safety === null) {
|
|
594
768
|
return !requireAll;
|
|
595
769
|
}
|
|
770
|
+
else if (decl.type === "VariableDeclarator") {
|
|
771
|
+
argDecls[i] = decl;
|
|
772
|
+
}
|
|
596
773
|
}
|
|
597
774
|
return true;
|
|
598
775
|
}
|
|
@@ -605,34 +782,91 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
605
782
|
}
|
|
606
783
|
if (allSafe && requireAll)
|
|
607
784
|
return true;
|
|
608
|
-
|
|
785
|
+
const callsSeen = new Set();
|
|
786
|
+
const modifiedDecls = new Set();
|
|
787
|
+
let modifiedUnknown = false;
|
|
609
788
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
610
789
|
// look for uses of "unsafe" args that occur after a call.
|
|
611
790
|
// use post to do the checking, because arguments are evaluated
|
|
612
791
|
// prior to the call, so eg "return f(x.y);" is fine, but
|
|
613
792
|
// "return f()+x.y" is not.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
793
|
+
const { pre, post, stack } = state;
|
|
794
|
+
try {
|
|
795
|
+
delete state.pre;
|
|
796
|
+
state.post = (node) => {
|
|
797
|
+
switch (node.type) {
|
|
798
|
+
case "AssignmentExpression":
|
|
799
|
+
case "UpdateExpression": {
|
|
800
|
+
const v = node.type == "UpdateExpression" ? node.argument : node.left;
|
|
801
|
+
if (v.type === "Identifier" && hasProperty(params, v.name)) {
|
|
802
|
+
// If a parameter is modified, we can't just substitute the
|
|
803
|
+
// argument wherever the parameter is used.
|
|
804
|
+
safeArgs[params[v.name]] = null;
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
if (modifiedUnknown)
|
|
808
|
+
break;
|
|
809
|
+
const [, results] = state.lookup(v);
|
|
810
|
+
if (results) {
|
|
811
|
+
results.forEach((r) => r.results.forEach((decl) => decl.type === "VariableDeclarator" && modifiedDecls.add(decl)));
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
modifiedUnknown = true;
|
|
815
|
+
}
|
|
816
|
+
break;
|
|
621
817
|
}
|
|
818
|
+
case "CallExpression":
|
|
819
|
+
case "NewExpression":
|
|
820
|
+
if (!modifiedUnknown) {
|
|
821
|
+
const [, results] = state.lookup(node.callee, null,
|
|
822
|
+
// calls are looked up as non-locals, but new is not
|
|
823
|
+
node.type === "CallExpression" ? func.stack : state.stack);
|
|
824
|
+
if (!results) {
|
|
825
|
+
const callee_name = node.callee.type === "Identifier"
|
|
826
|
+
? node.callee
|
|
827
|
+
: node.callee.type === "MemberExpression"
|
|
828
|
+
? isLookupCandidate(node.callee)
|
|
829
|
+
: null;
|
|
830
|
+
if (callee_name) {
|
|
831
|
+
const callees = state.allFunctions[callee_name.name];
|
|
832
|
+
if (callees) {
|
|
833
|
+
callees.forEach((callee) => callsSeen.add(callee));
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
modifiedUnknown = true;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
const callees = node.type === "CallExpression"
|
|
842
|
+
? findCallees(results)
|
|
843
|
+
: findCalleesForNew(results);
|
|
844
|
+
if (callees) {
|
|
845
|
+
callees.forEach((callee) => callsSeen.add(callee));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
break;
|
|
850
|
+
case "Identifier":
|
|
851
|
+
if (hasProperty(params, node.name) &&
|
|
852
|
+
!safeArgs[params[node.name]] &&
|
|
853
|
+
(modifiedUnknown ||
|
|
854
|
+
!argDecls[params[node.name]] ||
|
|
855
|
+
modifiedDecls.has(argDecls[params[node.name]]) ||
|
|
856
|
+
Array.from(callsSeen).some((callee) => functionMayModify(state, callee, argDecls[params[node.name]])))) {
|
|
857
|
+
safeArgs[params[node.name]] = null;
|
|
858
|
+
}
|
|
622
859
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
});
|
|
860
|
+
return null;
|
|
861
|
+
};
|
|
862
|
+
state.stack = func.stack;
|
|
863
|
+
state.traverse(func.node.body);
|
|
864
|
+
}
|
|
865
|
+
finally {
|
|
866
|
+
state.pre = pre;
|
|
867
|
+
state.post = post;
|
|
868
|
+
state.stack = stack;
|
|
869
|
+
}
|
|
636
870
|
return safeArgs;
|
|
637
871
|
}
|
|
638
872
|
function canInline(state, func, args) {
|
|
@@ -737,9 +971,6 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
737
971
|
state.pre = (node) => {
|
|
738
972
|
if (failed)
|
|
739
973
|
return [];
|
|
740
|
-
node.start = call.start;
|
|
741
|
-
node.end = call.end;
|
|
742
|
-
node.loc = call.loc;
|
|
743
974
|
if (replacements.has(node))
|
|
744
975
|
return false;
|
|
745
976
|
const result = pre(node, state);
|
|
@@ -754,6 +985,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
754
985
|
if (params[paramName] >= 0)
|
|
755
986
|
return null;
|
|
756
987
|
const name = renameVariable(state, locals, paramName) || paramName;
|
|
988
|
+
locals.map[name] = true;
|
|
757
989
|
return {
|
|
758
990
|
type: "VariableDeclarator",
|
|
759
991
|
id: { type: "Identifier", name },
|
|
@@ -772,31 +1004,49 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
772
1004
|
}
|
|
773
1005
|
return result;
|
|
774
1006
|
};
|
|
1007
|
+
const fixId = (node) => {
|
|
1008
|
+
if (state.inType)
|
|
1009
|
+
return null;
|
|
1010
|
+
if (hasProperty(params, node.name)) {
|
|
1011
|
+
const ix = params[node.name];
|
|
1012
|
+
if (ix >= 0) {
|
|
1013
|
+
const replacement = { ...call.arguments[ix] };
|
|
1014
|
+
replacements.add(replacement);
|
|
1015
|
+
return replacement;
|
|
1016
|
+
}
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
const replacement = fixNodeScope(state, node, func.stack);
|
|
1020
|
+
if (!replacement) {
|
|
1021
|
+
failed = true;
|
|
1022
|
+
inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
|
|
1023
|
+
}
|
|
1024
|
+
return replacement;
|
|
1025
|
+
};
|
|
775
1026
|
state.post = (node) => {
|
|
776
1027
|
if (failed)
|
|
777
1028
|
return post(node, state);
|
|
778
1029
|
let replacement = null;
|
|
779
1030
|
switch (node.type) {
|
|
780
|
-
case "
|
|
781
|
-
if (
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (ix >= 0) {
|
|
786
|
-
replacement = { ...call.arguments[ix] };
|
|
787
|
-
replacements.add(replacement);
|
|
788
|
-
return replacement;
|
|
1031
|
+
case "AssignmentExpression":
|
|
1032
|
+
if (node.left.type === "Identifier") {
|
|
1033
|
+
const rep = fixId(node.left);
|
|
1034
|
+
if (rep) {
|
|
1035
|
+
node.left = rep;
|
|
789
1036
|
}
|
|
790
|
-
break;
|
|
791
1037
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1038
|
+
break;
|
|
1039
|
+
case "UpdateExpression":
|
|
1040
|
+
if (node.argument.type === "Identifier") {
|
|
1041
|
+
const rep = fixId(node.argument);
|
|
1042
|
+
if (rep) {
|
|
1043
|
+
node.argument = rep;
|
|
1044
|
+
}
|
|
797
1045
|
}
|
|
798
1046
|
break;
|
|
799
|
-
|
|
1047
|
+
case "Identifier":
|
|
1048
|
+
replacement = fixId(node);
|
|
1049
|
+
break;
|
|
800
1050
|
}
|
|
801
1051
|
const ret = post(replacement || node, state);
|
|
802
1052
|
return ret === false || ret ? ret : replacement;
|
|
@@ -965,7 +1215,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
965
1215
|
}
|
|
966
1216
|
}
|
|
967
1217
|
}
|
|
968
|
-
const body =
|
|
1218
|
+
const body = cloneDeep(func.node.body);
|
|
969
1219
|
const safeArgs = getArgSafety(state, func, call.arguments, false);
|
|
970
1220
|
const params = Object.fromEntries(func.node.params.map((param, i) => {
|
|
971
1221
|
const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
|
|
@@ -1014,18 +1264,19 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
1014
1264
|
--block.body.length;
|
|
1015
1265
|
}
|
|
1016
1266
|
}
|
|
1267
|
+
withLocDeep(body, context, context, true);
|
|
1017
1268
|
return body;
|
|
1018
1269
|
}
|
|
1019
1270
|
function inliner_inlineFunction(state, func, call, context) {
|
|
1020
1271
|
if (context) {
|
|
1021
1272
|
return inlineWithArgs(state, func, call, context);
|
|
1022
1273
|
}
|
|
1023
|
-
const retArg =
|
|
1274
|
+
const retArg = cloneDeep(func.node.body.body[0].argument);
|
|
1024
1275
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
1025
1276
|
const map = fixupLocalsMap(state);
|
|
1026
1277
|
const ret = processInlineBody(state, func, call, retArg, params);
|
|
1027
1278
|
state.localsStack[state.localsStack.length - 1].map = map;
|
|
1028
|
-
return ret;
|
|
1279
|
+
return ret && withLocDeep(ret, call, call, true);
|
|
1029
1280
|
}
|
|
1030
1281
|
function applyTypeIfNeeded(node) {
|
|
1031
1282
|
if ("enumType" in node && node.enumType) {
|
|
@@ -1130,6 +1381,121 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
1130
1381
|
return null;
|
|
1131
1382
|
}
|
|
1132
1383
|
|
|
1384
|
+
;// CONCATENATED MODULE: ./src/pragma-checker.ts
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
function pragma_checker_pragmaChecker(state, ast, diagnostics) {
|
|
1389
|
+
const comments = ast.comments;
|
|
1390
|
+
if (!comments)
|
|
1391
|
+
return;
|
|
1392
|
+
diagnostics = diagnostics
|
|
1393
|
+
?.slice()
|
|
1394
|
+
.sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
|
|
1395
|
+
let diagIndex = 0;
|
|
1396
|
+
let index = -1;
|
|
1397
|
+
let comment;
|
|
1398
|
+
let matchers;
|
|
1399
|
+
const next = () => {
|
|
1400
|
+
while (++index < comments.length) {
|
|
1401
|
+
comment = comments[index];
|
|
1402
|
+
let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
|
|
1403
|
+
if (!match)
|
|
1404
|
+
continue;
|
|
1405
|
+
const kind = match[1];
|
|
1406
|
+
let str = match[2];
|
|
1407
|
+
matchers = [];
|
|
1408
|
+
while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
|
|
1409
|
+
matchers.push({ kind, quote: match[1], needle: match[2] });
|
|
1410
|
+
str = str.substring(match[0].length);
|
|
1411
|
+
if (!str.length)
|
|
1412
|
+
break;
|
|
1413
|
+
}
|
|
1414
|
+
if (!str.length)
|
|
1415
|
+
break;
|
|
1416
|
+
if (!matchers.length) {
|
|
1417
|
+
match = str.match(/^(\S+)\s+$/);
|
|
1418
|
+
if (match) {
|
|
1419
|
+
matchers.push({ kind, quote: '"', needle: match[1] });
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
diagnostic(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
const matcher = (quote, needle, haystack) => {
|
|
1427
|
+
if (quote == '"') {
|
|
1428
|
+
return haystack.includes(needle);
|
|
1429
|
+
}
|
|
1430
|
+
const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
|
|
1431
|
+
return re.test(haystack);
|
|
1432
|
+
};
|
|
1433
|
+
next();
|
|
1434
|
+
traverseAst(ast, (node) => {
|
|
1435
|
+
if (index >= comments.length)
|
|
1436
|
+
return false;
|
|
1437
|
+
if (node.start && node.start >= (comment.end || Infinity)) {
|
|
1438
|
+
const { kind, quote, needle } = matchers.shift();
|
|
1439
|
+
if (kind === "match") {
|
|
1440
|
+
const haystack = formatAst(node).replace(/([\r\n]|\s)+/g, " ");
|
|
1441
|
+
if (!matcher(quote, needle, haystack)) {
|
|
1442
|
+
matcher(quote, needle, haystack);
|
|
1443
|
+
diagnostic(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
else if (kind === "expect") {
|
|
1447
|
+
const locCmp = (a, b) => {
|
|
1448
|
+
if (!b)
|
|
1449
|
+
return -1;
|
|
1450
|
+
if (a.start.line < b.start.line)
|
|
1451
|
+
return -1;
|
|
1452
|
+
if (a.start.line === b.start.line &&
|
|
1453
|
+
a.start.column < b.start.column) {
|
|
1454
|
+
return -1;
|
|
1455
|
+
}
|
|
1456
|
+
if (a.end.line > b.end.line)
|
|
1457
|
+
return 1;
|
|
1458
|
+
if (a.end.line === b.end.line && a.end.column >= b.end.column) {
|
|
1459
|
+
return 1;
|
|
1460
|
+
}
|
|
1461
|
+
return 0;
|
|
1462
|
+
};
|
|
1463
|
+
let found = false;
|
|
1464
|
+
if (diagnostics) {
|
|
1465
|
+
while (true) {
|
|
1466
|
+
if (diagIndex >= diagnostics.length) {
|
|
1467
|
+
diagnostics = null;
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
const diag = diagnostics[diagIndex];
|
|
1471
|
+
const cmp = locCmp(diag.loc, node.loc);
|
|
1472
|
+
if (cmp > 0) {
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
diagIndex++;
|
|
1476
|
+
if (cmp < 0)
|
|
1477
|
+
continue;
|
|
1478
|
+
if (matcher(quote, needle, diag.message)) {
|
|
1479
|
+
found = true;
|
|
1480
|
+
diag.type = "INFO";
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
if (!found) {
|
|
1485
|
+
diagnostic(state, comment.loc, `Missing error message '${needle}`, "ERROR");
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (matchers.length) {
|
|
1489
|
+
// if we're checking a series of nodes, we need
|
|
1490
|
+
// to skip over this one.
|
|
1491
|
+
return false;
|
|
1492
|
+
}
|
|
1493
|
+
next();
|
|
1494
|
+
}
|
|
1495
|
+
return null;
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1133
1499
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
1134
1500
|
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
1135
1501
|
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
@@ -1652,6 +2018,7 @@ var priorityqueuejs = __webpack_require__(2789);
|
|
|
1652
2018
|
|
|
1653
2019
|
|
|
1654
2020
|
|
|
2021
|
+
|
|
1655
2022
|
/**
|
|
1656
2023
|
* This implements a pseudo Partial Redundancy Elimination
|
|
1657
2024
|
* pass. It isn't quite like traditional PRE because we're
|
|
@@ -1721,7 +2088,7 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1721
2088
|
return;
|
|
1722
2089
|
}
|
|
1723
2090
|
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
1724
|
-
const candidates = computeAttributes(head);
|
|
2091
|
+
const candidates = computeAttributes(state, head);
|
|
1725
2092
|
if (candidates) {
|
|
1726
2093
|
if (logging) {
|
|
1727
2094
|
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
@@ -1741,8 +2108,10 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1741
2108
|
let i = 0;
|
|
1742
2109
|
do {
|
|
1743
2110
|
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
1744
|
-
if (!identifiers.has(name))
|
|
2111
|
+
if (!identifiers.has(name)) {
|
|
2112
|
+
identifiers.add(name);
|
|
1745
2113
|
break;
|
|
2114
|
+
}
|
|
1746
2115
|
i++;
|
|
1747
2116
|
} while (true);
|
|
1748
2117
|
declMap.set(decl, name);
|
|
@@ -1833,11 +2202,16 @@ function buildPREGraph(state, func) {
|
|
|
1833
2202
|
case "ParenthesizedExpression":
|
|
1834
2203
|
break;
|
|
1835
2204
|
case "Literal":
|
|
1836
|
-
if (
|
|
1837
|
-
|
|
2205
|
+
if (refCost(node) > LocalRefCost) {
|
|
2206
|
+
const result = getNodeValue(node);
|
|
2207
|
+
const key = result[1] +
|
|
2208
|
+
(result[0].value === null
|
|
2209
|
+
? ""
|
|
2210
|
+
: "-" + result[0].value.toString());
|
|
2211
|
+
let decl = literals.get(key);
|
|
1838
2212
|
if (!decl) {
|
|
1839
2213
|
decl = node;
|
|
1840
|
-
literals.set(
|
|
2214
|
+
literals.set(key, decl);
|
|
1841
2215
|
}
|
|
1842
2216
|
return {
|
|
1843
2217
|
type: "ref",
|
|
@@ -1917,10 +2291,18 @@ function buildPREGraph(state, func) {
|
|
|
1917
2291
|
}
|
|
1918
2292
|
break;
|
|
1919
2293
|
}
|
|
1920
|
-
case "NewExpression":
|
|
1921
|
-
|
|
2294
|
+
case "NewExpression": {
|
|
2295
|
+
const [, results] = state.lookup(node.callee);
|
|
2296
|
+
const callees = results ? findCalleesForNew(results) : null;
|
|
1922
2297
|
liveDef(null, stmt);
|
|
1923
|
-
return { type: "mod", node, mayThrow };
|
|
2298
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2299
|
+
}
|
|
2300
|
+
case "CallExpression": {
|
|
2301
|
+
liveDef(null, stmt);
|
|
2302
|
+
const [, results] = state.lookup(node.callee);
|
|
2303
|
+
const callees = results ? findCallees(results) : null;
|
|
2304
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2305
|
+
}
|
|
1924
2306
|
default:
|
|
1925
2307
|
if (!isExpression(node))
|
|
1926
2308
|
break;
|
|
@@ -1936,12 +2318,6 @@ function buildPREGraph(state, func) {
|
|
|
1936
2318
|
function anticipatedDecls() {
|
|
1937
2319
|
return new Map();
|
|
1938
2320
|
}
|
|
1939
|
-
function cloneSet(ae) {
|
|
1940
|
-
return new Set(ae);
|
|
1941
|
-
}
|
|
1942
|
-
function mergeSet(a, b) {
|
|
1943
|
-
b.forEach((event) => a.add(event));
|
|
1944
|
-
}
|
|
1945
2321
|
function equalSet(a, b) {
|
|
1946
2322
|
if (a.size != b.size)
|
|
1947
2323
|
return false;
|
|
@@ -2020,6 +2396,7 @@ function refCost(node) {
|
|
|
2020
2396
|
switch (typeof node.value) {
|
|
2021
2397
|
case "string":
|
|
2022
2398
|
return 5;
|
|
2399
|
+
case "bigint":
|
|
2023
2400
|
case "number":
|
|
2024
2401
|
return 5;
|
|
2025
2402
|
case "boolean":
|
|
@@ -2081,7 +2458,7 @@ function candidateCost(candState) {
|
|
|
2081
2458
|
cost += defCost(candState.node) * boundarySize;
|
|
2082
2459
|
return cost;
|
|
2083
2460
|
}
|
|
2084
|
-
function computeAttributes(head) {
|
|
2461
|
+
function computeAttributes(state, head) {
|
|
2085
2462
|
const order = getPostOrder(head);
|
|
2086
2463
|
order.forEach((block, i) => {
|
|
2087
2464
|
block.order = i;
|
|
@@ -2190,7 +2567,9 @@ function computeAttributes(head) {
|
|
|
2190
2567
|
curState.forEach((candidates, decl) => {
|
|
2191
2568
|
if (decl.type === "VariableDeclarator" &&
|
|
2192
2569
|
decl.node.kind === "var" &&
|
|
2193
|
-
candidates.live
|
|
2570
|
+
candidates.live &&
|
|
2571
|
+
(!event.callees ||
|
|
2572
|
+
event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
|
|
2194
2573
|
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
2195
2574
|
candidates.live = false;
|
|
2196
2575
|
}
|
|
@@ -2379,20 +2758,26 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2379
2758
|
stmtStack.pop();
|
|
2380
2759
|
const events = nodeMap.get(node);
|
|
2381
2760
|
if (events) {
|
|
2382
|
-
|
|
2383
|
-
if (
|
|
2761
|
+
const ret = events.reduce((ret, event) => {
|
|
2762
|
+
if (event.type === "ref") {
|
|
2763
|
+
if (ret) {
|
|
2764
|
+
throw new Error(`ref found when there was already a replacement for this node`);
|
|
2765
|
+
}
|
|
2384
2766
|
if (node.type !== "Identifier" &&
|
|
2385
2767
|
node.type !== "MemberExpression" &&
|
|
2386
2768
|
node.type !== "Literal") {
|
|
2387
2769
|
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
2388
2770
|
}
|
|
2389
|
-
const name = declMap.get(
|
|
2771
|
+
const name = declMap.get(event.decl);
|
|
2390
2772
|
if (!name) {
|
|
2391
2773
|
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
2392
2774
|
}
|
|
2393
2775
|
return ident(name, node);
|
|
2394
2776
|
}
|
|
2395
|
-
|
|
2777
|
+
if (event.type === "def") {
|
|
2778
|
+
if (ret) {
|
|
2779
|
+
throw new Error(`def found when there was already a replacement for this node`);
|
|
2780
|
+
}
|
|
2396
2781
|
if (node.type !== "AssignmentExpression" &&
|
|
2397
2782
|
node.type !== "UpdateExpression") {
|
|
2398
2783
|
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
@@ -2400,7 +2785,7 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2400
2785
|
const target = node.type === "AssignmentExpression"
|
|
2401
2786
|
? node.left
|
|
2402
2787
|
: node.argument;
|
|
2403
|
-
const name = declMap.get(
|
|
2788
|
+
const name = declMap.get(event.decl);
|
|
2404
2789
|
if (!name) {
|
|
2405
2790
|
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
2406
2791
|
}
|
|
@@ -2419,20 +2804,24 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2419
2804
|
}
|
|
2420
2805
|
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
2421
2806
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2807
|
+
if (event.type === "mod") {
|
|
2808
|
+
if (!event.decl) {
|
|
2809
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
2810
|
+
}
|
|
2811
|
+
let pending = pendingMap.get(stmt);
|
|
2812
|
+
if (!pending) {
|
|
2813
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
2814
|
+
}
|
|
2815
|
+
pending.add(event);
|
|
2429
2816
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
pendingMap.set(stmt, (pending = new Set()));
|
|
2817
|
+
else {
|
|
2818
|
+
throw new Error(`Unexpected ${event.type} found`);
|
|
2433
2819
|
}
|
|
2434
|
-
|
|
2435
|
-
});
|
|
2820
|
+
return ret;
|
|
2821
|
+
}, null);
|
|
2822
|
+
if (ret) {
|
|
2823
|
+
return ret;
|
|
2824
|
+
}
|
|
2436
2825
|
}
|
|
2437
2826
|
const pending = pendingMap.get(node);
|
|
2438
2827
|
if (node.type === "SequenceExpression") {
|
|
@@ -2506,6 +2895,148 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2506
2895
|
});
|
|
2507
2896
|
}
|
|
2508
2897
|
|
|
2898
|
+
;// CONCATENATED MODULE: ./src/unused-exprs.ts
|
|
2899
|
+
|
|
2900
|
+
|
|
2901
|
+
|
|
2902
|
+
function unused_exprs_cleanupUnusedVars(state, node) {
|
|
2903
|
+
const [parent] = state.stack.slice(-1);
|
|
2904
|
+
if (parent.node !== node) {
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
if (parent.type != "BlockStatement") {
|
|
2908
|
+
throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
|
|
2909
|
+
}
|
|
2910
|
+
if (parent.decls) {
|
|
2911
|
+
let toRemove = null;
|
|
2912
|
+
Object.values(parent.decls).forEach((decls) => {
|
|
2913
|
+
if (decls.length === 1 &&
|
|
2914
|
+
decls[0].type === "VariableDeclarator" &&
|
|
2915
|
+
!decls[0].used) {
|
|
2916
|
+
if (!toRemove)
|
|
2917
|
+
toRemove = {};
|
|
2918
|
+
toRemove[decls[0].name] = decls[0];
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
if (toRemove) {
|
|
2922
|
+
const varDeclarations = new Map();
|
|
2923
|
+
traverseAst(node, null, (node) => {
|
|
2924
|
+
switch (node.type) {
|
|
2925
|
+
case "VariableDeclaration": {
|
|
2926
|
+
node.declarations.forEach((decl, i) => {
|
|
2927
|
+
const name = variableDeclarationName(decl.id);
|
|
2928
|
+
if (hasProperty(toRemove, name)) {
|
|
2929
|
+
const indices = varDeclarations.get(node);
|
|
2930
|
+
if (indices) {
|
|
2931
|
+
indices.push(i);
|
|
2932
|
+
}
|
|
2933
|
+
else {
|
|
2934
|
+
varDeclarations.set(node, [i]);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2938
|
+
break;
|
|
2939
|
+
}
|
|
2940
|
+
case "ExpressionStatement":
|
|
2941
|
+
if (node.expression.type === "AssignmentExpression") {
|
|
2942
|
+
if (node.expression.left.type === "Identifier" &&
|
|
2943
|
+
hasProperty(toRemove, node.expression.left.name)) {
|
|
2944
|
+
return unused(node.expression.right);
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
else if (node.expression.type === "UpdateExpression" &&
|
|
2948
|
+
node.expression.argument.type === "Identifier" &&
|
|
2949
|
+
hasProperty(toRemove, node.expression.argument.name)) {
|
|
2950
|
+
return false;
|
|
2951
|
+
}
|
|
2952
|
+
break;
|
|
2953
|
+
case "SequenceExpression": {
|
|
2954
|
+
for (let i = node.expressions.length; i--;) {
|
|
2955
|
+
const expr = node.expressions[i];
|
|
2956
|
+
if (expr.type === "AssignmentExpression") {
|
|
2957
|
+
if (expr.left.type === "Identifier" &&
|
|
2958
|
+
hasProperty(toRemove, expr.left.name)) {
|
|
2959
|
+
const rep = unused(expr.right);
|
|
2960
|
+
if (!rep.length) {
|
|
2961
|
+
node.expressions.splice(i, 1);
|
|
2962
|
+
}
|
|
2963
|
+
else {
|
|
2964
|
+
// Sequence expressions can only be assignments
|
|
2965
|
+
// or update expressions. Even calls aren't allowed
|
|
2966
|
+
toRemove[expr.left.name] = null;
|
|
2967
|
+
expr.operator = "=";
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
else if (expr.type === "UpdateExpression" &&
|
|
2972
|
+
expr.argument.type === "Identifier" &&
|
|
2973
|
+
hasProperty(toRemove, expr.argument.name)) {
|
|
2974
|
+
node.expressions.splice(i, 1);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
break;
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return null;
|
|
2981
|
+
});
|
|
2982
|
+
varDeclarations.forEach((indices, decl) => {
|
|
2983
|
+
let index = -1;
|
|
2984
|
+
for (let ii = indices.length, j = decl.declarations.length; ii--;) {
|
|
2985
|
+
const i = indices[ii];
|
|
2986
|
+
const vdecl = decl.declarations[i];
|
|
2987
|
+
const name = variableDeclarationName(vdecl.id);
|
|
2988
|
+
if (hasProperty(toRemove, name)) {
|
|
2989
|
+
const rep = vdecl.init ? unused(vdecl.init) : [];
|
|
2990
|
+
if (rep.length) {
|
|
2991
|
+
if (parent.node.type === "ForStatement") {
|
|
2992
|
+
// declarations whose inits have side effects
|
|
2993
|
+
// can't be deleted from for statements.
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2996
|
+
if (index < 0) {
|
|
2997
|
+
index = parent.node.body.findIndex((s) => s === decl);
|
|
2998
|
+
if (index < 0) {
|
|
2999
|
+
throw new Error(`Failed to find variable declaration for ${variableDeclarationName(vdecl.id)}`);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
if (j > i + 1) {
|
|
3003
|
+
const tail = {
|
|
3004
|
+
...decl,
|
|
3005
|
+
declarations: decl.declarations.slice(i + 1, j),
|
|
3006
|
+
};
|
|
3007
|
+
if (decl.loc && vdecl.loc) {
|
|
3008
|
+
tail.loc = { ...decl.loc, start: vdecl.loc.end };
|
|
3009
|
+
tail.start = vdecl.end;
|
|
3010
|
+
}
|
|
3011
|
+
rep.push(tail);
|
|
3012
|
+
}
|
|
3013
|
+
if (decl.loc && vdecl.loc) {
|
|
3014
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
3015
|
+
decl.end = vdecl.start;
|
|
3016
|
+
}
|
|
3017
|
+
decl.declarations.splice(i);
|
|
3018
|
+
parent.node.body.splice(index + 1, 0, ...rep);
|
|
3019
|
+
j = i;
|
|
3020
|
+
continue;
|
|
3021
|
+
}
|
|
3022
|
+
if (toRemove[name]) {
|
|
3023
|
+
j--;
|
|
3024
|
+
decl.declarations.splice(i, 1);
|
|
3025
|
+
if (i === j && decl.loc && vdecl.loc) {
|
|
3026
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
3027
|
+
decl.end = vdecl.start;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
delete vdecl.init;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
|
|
2509
3040
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
2510
3041
|
|
|
2511
3042
|
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
@@ -2570,14 +3101,16 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2570
3101
|
return checkResults(state.lookup(node), node);
|
|
2571
3102
|
}
|
|
2572
3103
|
break;
|
|
2573
|
-
case "MemberExpression":
|
|
2574
|
-
|
|
2575
|
-
|
|
3104
|
+
case "MemberExpression": {
|
|
3105
|
+
const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
|
|
3106
|
+
if (property) {
|
|
3107
|
+
if (!name || property.name === name) {
|
|
2576
3108
|
return checkResults(state.lookup(node), node) || ["object"];
|
|
2577
3109
|
}
|
|
2578
3110
|
return ["object"];
|
|
2579
3111
|
}
|
|
2580
3112
|
break;
|
|
3113
|
+
}
|
|
2581
3114
|
case "MethodDefinition": {
|
|
2582
3115
|
if (!state.inType) {
|
|
2583
3116
|
throw new Error("Method definition outside of type!");
|
|
@@ -2586,7 +3119,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2586
3119
|
node.params.forEach((param) => {
|
|
2587
3120
|
if (param.type == "BinaryExpression") {
|
|
2588
3121
|
state.traverse(param.right);
|
|
2589
|
-
state.inType = true;
|
|
2590
3122
|
}
|
|
2591
3123
|
});
|
|
2592
3124
|
}
|
|
@@ -2609,6 +3141,9 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2609
3141
|
|
|
2610
3142
|
|
|
2611
3143
|
|
|
3144
|
+
|
|
3145
|
+
|
|
3146
|
+
|
|
2612
3147
|
function collectClassInfo(state) {
|
|
2613
3148
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
2614
3149
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -2668,8 +3203,7 @@ function collectClassInfo(state) {
|
|
|
2668
3203
|
c.decls &&
|
|
2669
3204
|
Object.values(c.decls).forEach((funcs) => {
|
|
2670
3205
|
funcs.forEach((f) => {
|
|
2671
|
-
if (
|
|
2672
|
-
f.type === "FunctionDeclaration" &&
|
|
3206
|
+
if (f.type === "FunctionDeclaration" &&
|
|
2673
3207
|
hasProperty(cls.decls, f.name)) {
|
|
2674
3208
|
f.node.hasOverride = true;
|
|
2675
3209
|
}
|
|
@@ -2682,6 +3216,15 @@ function collectClassInfo(state) {
|
|
|
2682
3216
|
state.allClasses.forEach((elm) => {
|
|
2683
3217
|
if (elm.superClass)
|
|
2684
3218
|
markOverrides(elm, elm.superClass);
|
|
3219
|
+
if (elm.hasInvoke && elm.decls) {
|
|
3220
|
+
Object.values(elm.decls).forEach((funcs) => {
|
|
3221
|
+
funcs.forEach((f) => {
|
|
3222
|
+
if (f.type === "FunctionDeclaration" && !f.isStatic) {
|
|
3223
|
+
markInvokeClassMethod(f);
|
|
3224
|
+
}
|
|
3225
|
+
});
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
2685
3228
|
});
|
|
2686
3229
|
}
|
|
2687
3230
|
function getFileSources(fnMap) {
|
|
@@ -2721,7 +3264,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2721
3264
|
const preState = {
|
|
2722
3265
|
fnMap,
|
|
2723
3266
|
config,
|
|
2724
|
-
allFunctions:
|
|
3267
|
+
allFunctions: {},
|
|
2725
3268
|
allClasses: [],
|
|
2726
3269
|
shouldExclude(node) {
|
|
2727
3270
|
if ("attrs" in node &&
|
|
@@ -2751,11 +3294,6 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2751
3294
|
pre(node, state) {
|
|
2752
3295
|
switch (node.type) {
|
|
2753
3296
|
case "FunctionDeclaration":
|
|
2754
|
-
if (markApi) {
|
|
2755
|
-
node.body = null;
|
|
2756
|
-
break;
|
|
2757
|
-
}
|
|
2758
|
-
// falls through
|
|
2759
3297
|
case "ModuleDeclaration":
|
|
2760
3298
|
case "ClassDeclaration": {
|
|
2761
3299
|
const [scope] = state.stack.slice(-1);
|
|
@@ -2766,7 +3304,18 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2766
3304
|
(scope.node.attrs &&
|
|
2767
3305
|
scope.node.attrs.access &&
|
|
2768
3306
|
scope.node.attrs.access.includes("static"));
|
|
2769
|
-
|
|
3307
|
+
if (markApi) {
|
|
3308
|
+
node.body = null;
|
|
3309
|
+
scope.info = getApiFunctionInfo(scope);
|
|
3310
|
+
delete scope.stack;
|
|
3311
|
+
}
|
|
3312
|
+
const allFuncs = state.allFunctions;
|
|
3313
|
+
if (!hasProperty(allFuncs, scope.name)) {
|
|
3314
|
+
allFuncs[scope.name] = [scope];
|
|
3315
|
+
}
|
|
3316
|
+
else {
|
|
3317
|
+
allFuncs[scope.name].push(scope);
|
|
3318
|
+
}
|
|
2770
3319
|
}
|
|
2771
3320
|
else if (scope.type === "ClassDeclaration") {
|
|
2772
3321
|
state.allClasses.push(scope);
|
|
@@ -2791,7 +3340,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2791
3340
|
value.hasTests = hasTests;
|
|
2792
3341
|
});
|
|
2793
3342
|
delete state.shouldExclude;
|
|
2794
|
-
delete state.
|
|
3343
|
+
delete state.pre;
|
|
2795
3344
|
collectClassInfo(state);
|
|
2796
3345
|
const diagnosticType = config?.checkInvalidSymbols !== "OFF"
|
|
2797
3346
|
? config?.checkInvalidSymbols || "WARNING"
|
|
@@ -2814,6 +3363,8 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2814
3363
|
});
|
|
2815
3364
|
});
|
|
2816
3365
|
}
|
|
3366
|
+
state.exposed = state.nextExposed;
|
|
3367
|
+
state.nextExposed = {};
|
|
2817
3368
|
return state;
|
|
2818
3369
|
}
|
|
2819
3370
|
function compareLiteralLike(a, b) {
|
|
@@ -2859,57 +3410,16 @@ function getLiteralNode(node) {
|
|
|
2859
3410
|
if (node.argument.type != "Literal")
|
|
2860
3411
|
return null;
|
|
2861
3412
|
switch (node.operator) {
|
|
2862
|
-
case "-":
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
value: -node.argument.value,
|
|
2867
|
-
raw: "-" + node.argument.value,
|
|
2868
|
-
enumType: node.enumType,
|
|
2869
|
-
};
|
|
3413
|
+
case "-": {
|
|
3414
|
+
const [arg, type] = ast_getNodeValue(node.argument);
|
|
3415
|
+
if (type === "Number" || type === "Long") {
|
|
3416
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2870
3417
|
}
|
|
3418
|
+
}
|
|
2871
3419
|
}
|
|
2872
3420
|
}
|
|
2873
3421
|
return null;
|
|
2874
3422
|
}
|
|
2875
|
-
function getNodeValue(node) {
|
|
2876
|
-
if (node.type == "BinaryExpression" &&
|
|
2877
|
-
node.operator == "as" &&
|
|
2878
|
-
node.right.type == "TypeSpecList" &&
|
|
2879
|
-
node.right.ts.length == 1 &&
|
|
2880
|
-
typeof node.right.ts[0] == "string") {
|
|
2881
|
-
// this is a cast we inserted to retain the type of an enum
|
|
2882
|
-
// any arithmetic on it will revert to "Number", or "Long",
|
|
2883
|
-
// so just ignore it.
|
|
2884
|
-
return getNodeValue(node.left);
|
|
2885
|
-
}
|
|
2886
|
-
if (node.type != "Literal") {
|
|
2887
|
-
return [null, null];
|
|
2888
|
-
}
|
|
2889
|
-
let type = node.value === null ? "Null" : typeof node.value;
|
|
2890
|
-
if (type === "number") {
|
|
2891
|
-
const match = node.raw && LiteralIntegerRe.exec(node.raw);
|
|
2892
|
-
if (match) {
|
|
2893
|
-
type = match[2] == "l" ? "Long" : "Number";
|
|
2894
|
-
}
|
|
2895
|
-
else if (node.raw && node.raw.endsWith("d")) {
|
|
2896
|
-
type = "Double";
|
|
2897
|
-
}
|
|
2898
|
-
else {
|
|
2899
|
-
type = "Float";
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
else if (type === "string") {
|
|
2903
|
-
type = "String";
|
|
2904
|
-
}
|
|
2905
|
-
else if (type === "boolean") {
|
|
2906
|
-
type = "Boolean";
|
|
2907
|
-
}
|
|
2908
|
-
else {
|
|
2909
|
-
type = "Unknown";
|
|
2910
|
-
}
|
|
2911
|
-
return [node, type];
|
|
2912
|
-
}
|
|
2913
3423
|
function fullTypeName(state, tsp) {
|
|
2914
3424
|
if (typeof tsp.name === "string") {
|
|
2915
3425
|
return tsp.name;
|
|
@@ -2953,6 +3463,46 @@ function isBooleanExpression(state, node) {
|
|
|
2953
3463
|
}
|
|
2954
3464
|
return false;
|
|
2955
3465
|
}
|
|
3466
|
+
function replacementLiteral(arg, value, type) {
|
|
3467
|
+
if (typeof value === "boolean") {
|
|
3468
|
+
type = "Boolean";
|
|
3469
|
+
}
|
|
3470
|
+
else if (type === "Number") {
|
|
3471
|
+
value = Number(BigInt.asIntN(32, BigInt(value)));
|
|
3472
|
+
}
|
|
3473
|
+
else if (type === "Long") {
|
|
3474
|
+
value = BigInt.asIntN(64, BigInt(value));
|
|
3475
|
+
}
|
|
3476
|
+
return {
|
|
3477
|
+
...arg,
|
|
3478
|
+
value,
|
|
3479
|
+
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
const operators = {
|
|
3483
|
+
"+": (left, right) => left + right,
|
|
3484
|
+
"-": (left, right) => left - right,
|
|
3485
|
+
"*": (left, right) => left * right,
|
|
3486
|
+
"/": (left, right) => left / right,
|
|
3487
|
+
"%": (left, right) => left % right,
|
|
3488
|
+
"&": (left, right) => left & right,
|
|
3489
|
+
"|": (left, right) => left | right,
|
|
3490
|
+
"^": (left, right) => left ^ right,
|
|
3491
|
+
"<<": (left, right) => left << (right & 127n),
|
|
3492
|
+
">>": (left, right) => left >> (right & 127n),
|
|
3493
|
+
"==": (left, right) =>
|
|
3494
|
+
// two string literals will compare unequal, becuase string
|
|
3495
|
+
// equality is object equality.
|
|
3496
|
+
typeof left === "string" ? false : left === right,
|
|
3497
|
+
"!=": (left, right) => typeof left === "string" ? true : left !== right,
|
|
3498
|
+
"<=": (left, right) => left <= right,
|
|
3499
|
+
">=": (left, right) => left >= right,
|
|
3500
|
+
"<": (left, right) => left < right,
|
|
3501
|
+
">": (left, right) => left > right,
|
|
3502
|
+
as: null,
|
|
3503
|
+
instanceof: null,
|
|
3504
|
+
has: null,
|
|
3505
|
+
};
|
|
2956
3506
|
function optimizeNode(state, node) {
|
|
2957
3507
|
switch (node.type) {
|
|
2958
3508
|
case "UnaryExpression": {
|
|
@@ -2967,29 +3517,17 @@ function optimizeNode(state, node) {
|
|
|
2967
3517
|
break;
|
|
2968
3518
|
case "-":
|
|
2969
3519
|
if (type === "Number" || type === "Long") {
|
|
2970
|
-
return
|
|
2971
|
-
...arg,
|
|
2972
|
-
value: -arg.value,
|
|
2973
|
-
raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
|
|
2974
|
-
};
|
|
3520
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2975
3521
|
}
|
|
2976
3522
|
break;
|
|
2977
3523
|
case "!":
|
|
2978
3524
|
case "~":
|
|
2979
3525
|
{
|
|
2980
|
-
let value;
|
|
2981
3526
|
if (type === "Number" || type === "Long") {
|
|
2982
|
-
|
|
2983
|
-
}
|
|
2984
|
-
else if (type === "Boolean" && node.operator == "!") {
|
|
2985
|
-
value = !arg.value;
|
|
3527
|
+
return replacementLiteral(arg, ~BigInt(arg.value), type);
|
|
2986
3528
|
}
|
|
2987
|
-
if (
|
|
2988
|
-
return
|
|
2989
|
-
...arg,
|
|
2990
|
-
value,
|
|
2991
|
-
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
2992
|
-
};
|
|
3529
|
+
if (type === "Boolean" && node.operator == "!") {
|
|
3530
|
+
return replacementLiteral(arg, !arg.value, type);
|
|
2993
3531
|
}
|
|
2994
3532
|
}
|
|
2995
3533
|
break;
|
|
@@ -2997,27 +3535,6 @@ function optimizeNode(state, node) {
|
|
|
2997
3535
|
break;
|
|
2998
3536
|
}
|
|
2999
3537
|
case "BinaryExpression": {
|
|
3000
|
-
const operators = {
|
|
3001
|
-
"+": (left, right) => left + right,
|
|
3002
|
-
"-": (left, right) => left - right,
|
|
3003
|
-
"*": (left, right) => left * right,
|
|
3004
|
-
"/": (left, right) => Math.trunc(left / right),
|
|
3005
|
-
"%": (left, right) => left % right,
|
|
3006
|
-
"&": (left, right, type) => type === "Number" ? left & right : null,
|
|
3007
|
-
"|": (left, right, type) => type === "Number" ? left | right : null,
|
|
3008
|
-
"^": (left, right, type) => type === "Number" ? left ^ right : null,
|
|
3009
|
-
"<<": (left, right, type) => type === "Number" ? left << right : null,
|
|
3010
|
-
">>": (left, right, type) => type === "Number" ? left >> right : null,
|
|
3011
|
-
"==": (left, right) => left == right,
|
|
3012
|
-
"!=": (left, right) => left != right,
|
|
3013
|
-
"<=": (left, right) => left <= right,
|
|
3014
|
-
">=": (left, right) => left >= right,
|
|
3015
|
-
"<": (left, right) => left < right,
|
|
3016
|
-
">": (left, right) => left > right,
|
|
3017
|
-
as: null,
|
|
3018
|
-
instanceof: null,
|
|
3019
|
-
has: null,
|
|
3020
|
-
};
|
|
3021
3538
|
const op = operators[node.operator];
|
|
3022
3539
|
if (op) {
|
|
3023
3540
|
const [left, left_type] = getNodeValue(node.left);
|
|
@@ -3025,23 +3542,22 @@ function optimizeNode(state, node) {
|
|
|
3025
3542
|
if (!left || !right)
|
|
3026
3543
|
break;
|
|
3027
3544
|
let value = null;
|
|
3028
|
-
|
|
3029
|
-
|
|
3545
|
+
let type;
|
|
3546
|
+
if ((left_type != "Number" && left_type != "Long") ||
|
|
3547
|
+
left_type != right_type) {
|
|
3030
3548
|
if (node.operator !== "==" && node.operator !== "!=") {
|
|
3031
3549
|
break;
|
|
3032
3550
|
}
|
|
3033
3551
|
value = operators[node.operator](left.value, right.value);
|
|
3552
|
+
type = "Boolean";
|
|
3034
3553
|
}
|
|
3035
3554
|
else {
|
|
3036
|
-
|
|
3555
|
+
type = left_type;
|
|
3556
|
+
value = op(BigInt(left.value), BigInt(right.value));
|
|
3037
3557
|
}
|
|
3038
3558
|
if (value === null)
|
|
3039
3559
|
break;
|
|
3040
|
-
return
|
|
3041
|
-
...left,
|
|
3042
|
-
value,
|
|
3043
|
-
raw: value.toString() + (left_type === "Long" ? "l" : ""),
|
|
3044
|
-
};
|
|
3560
|
+
return replacementLiteral(left, value, type);
|
|
3045
3561
|
}
|
|
3046
3562
|
break;
|
|
3047
3563
|
}
|
|
@@ -3051,7 +3567,8 @@ function optimizeNode(state, node) {
|
|
|
3051
3567
|
break;
|
|
3052
3568
|
const falsy = left.value === false ||
|
|
3053
3569
|
left.value === null ||
|
|
3054
|
-
(
|
|
3570
|
+
((left_type === "Number" || left_type === "Long") &&
|
|
3571
|
+
(left.value === 0 || left.value === 0n));
|
|
3055
3572
|
if (falsy === (node.operator === "&&")) {
|
|
3056
3573
|
return left;
|
|
3057
3574
|
}
|
|
@@ -3092,9 +3609,7 @@ function evaluateFunction(state, func, args) {
|
|
|
3092
3609
|
const paramValues = args &&
|
|
3093
3610
|
Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
|
|
3094
3611
|
let ret = null;
|
|
3095
|
-
const body = args
|
|
3096
|
-
? JSON.parse(JSON.stringify(func.body))
|
|
3097
|
-
: func.body;
|
|
3612
|
+
const body = args ? cloneDeep(func.body) : func.body;
|
|
3098
3613
|
try {
|
|
3099
3614
|
traverseAst(body, (node) => {
|
|
3100
3615
|
switch (node.type) {
|
|
@@ -3145,12 +3660,10 @@ function markFunctionCalled(state, func) {
|
|
|
3145
3660
|
pushUnique(state.calledFunctions[func.id.name], func);
|
|
3146
3661
|
}
|
|
3147
3662
|
async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
3148
|
-
const state =
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
calledFunctions: {},
|
|
3153
|
-
};
|
|
3663
|
+
const state = (await analyze(fnMap, barrelList, config));
|
|
3664
|
+
state.localsStack = [{}];
|
|
3665
|
+
state.calledFunctions = {};
|
|
3666
|
+
state.usedByName = {};
|
|
3154
3667
|
const replace = (node, old) => {
|
|
3155
3668
|
if (node === false || node === null)
|
|
3156
3669
|
return node;
|
|
@@ -3239,6 +3752,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3239
3752
|
f.type == "FunctionDeclaration" &&
|
|
3240
3753
|
maybeCalled(f.node))) ||
|
|
3241
3754
|
(sc.superClass && checkInherited(sc, name))));
|
|
3755
|
+
const renamer = (idnode) => {
|
|
3756
|
+
const ident = idnode.type === "Identifier" ? idnode : idnode.left;
|
|
3757
|
+
const locals = topLocals();
|
|
3758
|
+
const { map } = locals;
|
|
3759
|
+
if (map) {
|
|
3760
|
+
const declName = ident.name;
|
|
3761
|
+
const name = renameVariable(state, locals, declName);
|
|
3762
|
+
if (name) {
|
|
3763
|
+
const [, results] = state.lookupValue(ident);
|
|
3764
|
+
if (!results) {
|
|
3765
|
+
throw new Error(`Didn't find local ${declName} which needed renaming`);
|
|
3766
|
+
}
|
|
3767
|
+
if (results.length !== 1) {
|
|
3768
|
+
throw new Error(`Lookup of local ${declName} found more than one result`);
|
|
3769
|
+
}
|
|
3770
|
+
const parent = results[0].parent;
|
|
3771
|
+
if (!parent) {
|
|
3772
|
+
throw new Error(`No parent in lookup of local ${declName}`);
|
|
3773
|
+
}
|
|
3774
|
+
const decls = parent.decls;
|
|
3775
|
+
if (!decls || !hasProperty(decls, declName)) {
|
|
3776
|
+
throw new Error(`Missing decls in lookup of local ${declName}`);
|
|
3777
|
+
}
|
|
3778
|
+
if (hasProperty(decls, name)) {
|
|
3779
|
+
throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
|
|
3780
|
+
}
|
|
3781
|
+
if (decls[declName].length === 1) {
|
|
3782
|
+
decls[name] = decls[declName];
|
|
3783
|
+
delete decls[declName];
|
|
3784
|
+
}
|
|
3785
|
+
else {
|
|
3786
|
+
let i = decls[declName].length;
|
|
3787
|
+
while (i--) {
|
|
3788
|
+
const decl = decls[declName][i];
|
|
3789
|
+
if (decl === idnode ||
|
|
3790
|
+
(decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
|
|
3791
|
+
decls[declName].splice(i, 1);
|
|
3792
|
+
decls[name] = [decl];
|
|
3793
|
+
break;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
if (i < 0) {
|
|
3797
|
+
throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
ident.name = name;
|
|
3801
|
+
}
|
|
3802
|
+
else {
|
|
3803
|
+
map[declName] = true;
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
};
|
|
3242
3807
|
state.pre = (node) => {
|
|
3243
3808
|
switch (node.type) {
|
|
3244
3809
|
case "ConditionalExpression":
|
|
@@ -3259,7 +3824,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3259
3824
|
result = !!value.value;
|
|
3260
3825
|
}
|
|
3261
3826
|
if (result !== null) {
|
|
3262
|
-
node.test = {
|
|
3827
|
+
node.test = {
|
|
3828
|
+
type: "Literal",
|
|
3829
|
+
value: result,
|
|
3830
|
+
raw: result.toString(),
|
|
3831
|
+
};
|
|
3263
3832
|
if (node.type === "IfStatement" ||
|
|
3264
3833
|
node.type === "ConditionalExpression") {
|
|
3265
3834
|
return [result ? "consequent" : "alternate"];
|
|
@@ -3278,7 +3847,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3278
3847
|
return null;
|
|
3279
3848
|
}
|
|
3280
3849
|
case "EnumDeclaration":
|
|
3281
|
-
return
|
|
3850
|
+
return [];
|
|
3282
3851
|
case "ForStatement": {
|
|
3283
3852
|
const map = topLocals().map;
|
|
3284
3853
|
if (map) {
|
|
@@ -3287,43 +3856,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3287
3856
|
break;
|
|
3288
3857
|
}
|
|
3289
3858
|
case "VariableDeclarator": {
|
|
3290
|
-
|
|
3291
|
-
const { map } = locals;
|
|
3292
|
-
if (map) {
|
|
3293
|
-
const declName = variableDeclarationName(node.id);
|
|
3294
|
-
const name = renameVariable(state, locals, declName);
|
|
3295
|
-
if (name) {
|
|
3296
|
-
if (node.id.type === "Identifier") {
|
|
3297
|
-
node.id.name = name;
|
|
3298
|
-
}
|
|
3299
|
-
else {
|
|
3300
|
-
node.id.left.name = name;
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
else {
|
|
3304
|
-
map[declName] = true;
|
|
3305
|
-
}
|
|
3306
|
-
}
|
|
3859
|
+
renamer(node.id);
|
|
3307
3860
|
return ["init"];
|
|
3308
3861
|
}
|
|
3309
3862
|
case "CatchClause":
|
|
3310
3863
|
if (node.param) {
|
|
3311
3864
|
state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
|
|
3312
|
-
|
|
3313
|
-
const map = locals.map;
|
|
3314
|
-
const declName = variableDeclarationName(node.param);
|
|
3315
|
-
const name = renameVariable(state, locals, declName);
|
|
3316
|
-
if (name) {
|
|
3317
|
-
if (node.param.type === "Identifier") {
|
|
3318
|
-
node.param.name = name;
|
|
3319
|
-
}
|
|
3320
|
-
else {
|
|
3321
|
-
node.param.left.name = name;
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
else {
|
|
3325
|
-
map[declName] = true;
|
|
3326
|
-
}
|
|
3865
|
+
renamer(node.param);
|
|
3327
3866
|
return ["body"];
|
|
3328
3867
|
}
|
|
3329
3868
|
break;
|
|
@@ -3339,14 +3878,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3339
3878
|
break;
|
|
3340
3879
|
case "UnaryExpression":
|
|
3341
3880
|
if (node.operator == ":") {
|
|
3342
|
-
//
|
|
3343
|
-
//
|
|
3344
|
-
// indirectly, so we can't remove any enums or
|
|
3345
|
-
// constants with that name (we can still replace
|
|
3346
|
-
// uses of those constants though).
|
|
3347
|
-
state.exposed[node.argument.name] = true;
|
|
3348
|
-
// In any case, we can't replace *this* use of the
|
|
3349
|
-
// symbol with its value...
|
|
3881
|
+
// node.argument is not a normal identifier.
|
|
3882
|
+
// don't visit it.
|
|
3350
3883
|
return [];
|
|
3351
3884
|
}
|
|
3352
3885
|
break;
|
|
@@ -3358,29 +3891,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3358
3891
|
if (typeof name === "string") {
|
|
3359
3892
|
node.name = name;
|
|
3360
3893
|
}
|
|
3894
|
+
const [, results] = state.lookupValue(node);
|
|
3895
|
+
if (results) {
|
|
3896
|
+
if (results.length !== 1 || results[0].results.length !== 1) {
|
|
3897
|
+
throw new Error(`Local ${node.name} had multiple lookup results`);
|
|
3898
|
+
}
|
|
3899
|
+
const parent = results[0].parent;
|
|
3900
|
+
if (!parent) {
|
|
3901
|
+
throw new Error(`Local ${node.name} had no parent`);
|
|
3902
|
+
}
|
|
3903
|
+
const decl = results[0].results[0];
|
|
3904
|
+
if (parent.type === "FunctionDeclaration" ||
|
|
3905
|
+
decl.type !== "VariableDeclarator") {
|
|
3906
|
+
// we can't optimize away function or catch parameters
|
|
3907
|
+
return [];
|
|
3908
|
+
}
|
|
3909
|
+
if (parent.type !== "BlockStatement") {
|
|
3910
|
+
throw new Error(`Local ${node.name} was not declared at block scope(??)`);
|
|
3911
|
+
}
|
|
3912
|
+
decl.used = true;
|
|
3913
|
+
}
|
|
3361
3914
|
}
|
|
3362
3915
|
}
|
|
3363
3916
|
if (hasProperty(state.index, node.name)) {
|
|
3364
3917
|
if (!lookupAndReplace(node)) {
|
|
3365
|
-
state.
|
|
3918
|
+
state.usedByName[node.name] = true;
|
|
3366
3919
|
}
|
|
3367
3920
|
}
|
|
3368
3921
|
return [];
|
|
3369
3922
|
}
|
|
3370
|
-
case "MemberExpression":
|
|
3371
|
-
|
|
3372
|
-
|
|
3923
|
+
case "MemberExpression": {
|
|
3924
|
+
const property = isLookupCandidate(node);
|
|
3925
|
+
if (property) {
|
|
3926
|
+
if (hasProperty(state.index, property.name)) {
|
|
3373
3927
|
if (lookupAndReplace(node)) {
|
|
3374
3928
|
return false;
|
|
3375
3929
|
}
|
|
3376
3930
|
else {
|
|
3377
|
-
state.
|
|
3931
|
+
state.usedByName[property.name] = true;
|
|
3378
3932
|
}
|
|
3379
3933
|
}
|
|
3380
3934
|
// Don't optimize the property.
|
|
3381
3935
|
return ["object"];
|
|
3382
3936
|
}
|
|
3383
3937
|
break;
|
|
3938
|
+
}
|
|
3939
|
+
case "AssignmentExpression":
|
|
3940
|
+
case "UpdateExpression": {
|
|
3941
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
3942
|
+
if (lhs.type === "Identifier") {
|
|
3943
|
+
const map = topLocals().map;
|
|
3944
|
+
if (map) {
|
|
3945
|
+
if (hasProperty(map, lhs.name)) {
|
|
3946
|
+
const name = map[lhs.name];
|
|
3947
|
+
if (typeof name === "string") {
|
|
3948
|
+
lhs.name = name;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
else if (lhs.type === "MemberExpression") {
|
|
3954
|
+
state.traverse(lhs.object);
|
|
3955
|
+
if (lhs.computed) {
|
|
3956
|
+
state.traverse(lhs.property);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
return node.type === "AssignmentExpression" ? ["right"] : [];
|
|
3960
|
+
}
|
|
3384
3961
|
case "BlockStatement": {
|
|
3385
3962
|
const map = topLocals().map;
|
|
3386
3963
|
if (map) {
|
|
@@ -3396,7 +3973,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3396
3973
|
node.params &&
|
|
3397
3974
|
node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
|
|
3398
3975
|
state.localsStack.push({ node, map });
|
|
3399
|
-
const [parent] = state.stack.slice(-2);
|
|
3976
|
+
const [parent, self] = state.stack.slice(-2);
|
|
3977
|
+
if (state.currentFunction) {
|
|
3978
|
+
throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
|
|
3979
|
+
}
|
|
3980
|
+
state.currentFunction = self;
|
|
3400
3981
|
if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
|
|
3401
3982
|
let used = false;
|
|
3402
3983
|
if (node.id.name == "initialize") {
|
|
@@ -3423,10 +4004,23 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3423
4004
|
return replace(opt, node);
|
|
3424
4005
|
}
|
|
3425
4006
|
switch (node.type) {
|
|
4007
|
+
case "FunctionDeclaration":
|
|
4008
|
+
if (!state.currentFunction) {
|
|
4009
|
+
throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
|
|
4010
|
+
}
|
|
4011
|
+
state.currentFunction.info = state.currentFunction.next_info;
|
|
4012
|
+
delete state.currentFunction.next_info;
|
|
4013
|
+
delete state.currentFunction;
|
|
4014
|
+
break;
|
|
3426
4015
|
case "BlockStatement":
|
|
3427
4016
|
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
3428
4017
|
node.body.splice(0, 1, ...node.body[0].body);
|
|
3429
4018
|
}
|
|
4019
|
+
// fall through
|
|
4020
|
+
case "ForStatement":
|
|
4021
|
+
if (locals.map) {
|
|
4022
|
+
cleanupUnusedVars(state, node);
|
|
4023
|
+
}
|
|
3430
4024
|
break;
|
|
3431
4025
|
case "ConditionalExpression":
|
|
3432
4026
|
case "IfStatement":
|
|
@@ -3459,17 +4053,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3459
4053
|
return replace(optimizeCall(state, node.argument, node), node.argument);
|
|
3460
4054
|
}
|
|
3461
4055
|
break;
|
|
4056
|
+
case "NewExpression":
|
|
4057
|
+
if (state.currentFunction) {
|
|
4058
|
+
const [, results] = state.lookup(node.callee);
|
|
4059
|
+
if (results) {
|
|
4060
|
+
recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
|
|
4061
|
+
}
|
|
4062
|
+
else {
|
|
4063
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
break;
|
|
3462
4067
|
case "CallExpression": {
|
|
3463
4068
|
return replace(optimizeCall(state, node, null), node);
|
|
3464
4069
|
}
|
|
3465
|
-
case "AssignmentExpression":
|
|
3466
|
-
if (node.operator === "=" &&
|
|
3467
|
-
node.left.type === "Identifier" &&
|
|
3468
|
-
node.right.type === "Identifier" &&
|
|
3469
|
-
node.left.name === node.right.name) {
|
|
3470
|
-
return { type: "Literal", value: null, raw: "null" };
|
|
3471
|
-
}
|
|
3472
|
-
break;
|
|
3473
4070
|
case "VariableDeclaration": {
|
|
3474
4071
|
const locals = topLocals();
|
|
3475
4072
|
if (locals.map &&
|
|
@@ -3543,6 +4140,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3543
4140
|
}
|
|
3544
4141
|
}
|
|
3545
4142
|
break;
|
|
4143
|
+
case "AssignmentExpression":
|
|
4144
|
+
if (node.operator === "=" &&
|
|
4145
|
+
node.left.type === "Identifier" &&
|
|
4146
|
+
node.right.type === "Identifier" &&
|
|
4147
|
+
node.left.name === node.right.name) {
|
|
4148
|
+
return { type: "Literal", value: null, raw: "null" };
|
|
4149
|
+
}
|
|
4150
|
+
// fall through;
|
|
4151
|
+
case "UpdateExpression":
|
|
4152
|
+
if (state.currentFunction) {
|
|
4153
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
4154
|
+
const [, results] = state.lookup(lhs);
|
|
4155
|
+
if (results) {
|
|
4156
|
+
recordModifiedDecls(state.currentFunction, results);
|
|
4157
|
+
}
|
|
4158
|
+
else {
|
|
4159
|
+
const id = lhs.type === "Identifier" ? lhs : isLookupCandidate(lhs);
|
|
4160
|
+
if (id) {
|
|
4161
|
+
recordModifiedName(state.currentFunction, id.name);
|
|
4162
|
+
}
|
|
4163
|
+
else {
|
|
4164
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
break;
|
|
3546
4169
|
}
|
|
3547
4170
|
return null;
|
|
3548
4171
|
};
|
|
@@ -3550,13 +4173,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3550
4173
|
collectNamespaces(f.ast, state);
|
|
3551
4174
|
});
|
|
3552
4175
|
state.calledFunctions = {};
|
|
3553
|
-
state.exposed =
|
|
4176
|
+
state.exposed = state.nextExposed;
|
|
4177
|
+
state.nextExposed = {};
|
|
3554
4178
|
Object.values(fnMap).forEach((f) => {
|
|
3555
4179
|
collectNamespaces(f.ast, state);
|
|
3556
4180
|
});
|
|
4181
|
+
state.exposed = state.nextExposed;
|
|
4182
|
+
state.nextExposed = {};
|
|
3557
4183
|
delete state.pre;
|
|
3558
4184
|
delete state.post;
|
|
3559
|
-
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
4185
|
+
Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
|
|
3560
4186
|
const cleanup = (node) => {
|
|
3561
4187
|
switch (node.type) {
|
|
3562
4188
|
case "ThisExpression":
|
|
@@ -3566,7 +4192,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3566
4192
|
if (node.members.every((m) => {
|
|
3567
4193
|
const name = "name" in m ? m.name : m.id.name;
|
|
3568
4194
|
return (hasProperty(state.index, name) &&
|
|
3569
|
-
!hasProperty(state.exposed, name)
|
|
4195
|
+
!hasProperty(state.exposed, name) &&
|
|
4196
|
+
!hasProperty(state.usedByName, name));
|
|
3570
4197
|
})) {
|
|
3571
4198
|
node.enumType = [
|
|
3572
4199
|
...new Set(node.members.map((m) => {
|
|
@@ -3609,7 +4236,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3609
4236
|
case "VariableDeclaration": {
|
|
3610
4237
|
node.declarations = node.declarations.filter((d) => {
|
|
3611
4238
|
const name = variableDeclarationName(d.id);
|
|
3612
|
-
return (!hasProperty(state.index, name) ||
|
|
4239
|
+
return (!hasProperty(state.index, name) ||
|
|
4240
|
+
hasProperty(state.exposed, name) ||
|
|
4241
|
+
hasProperty(state.usedByName, name));
|
|
3613
4242
|
});
|
|
3614
4243
|
if (!node.declarations.length) {
|
|
3615
4244
|
return false;
|
|
@@ -3623,6 +4252,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3623
4252
|
break;
|
|
3624
4253
|
case "FunctionDeclaration":
|
|
3625
4254
|
if (!maybeCalled(node)) {
|
|
4255
|
+
if (node.attrs &&
|
|
4256
|
+
node.attrs.attributes &&
|
|
4257
|
+
node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
3626
4260
|
return false;
|
|
3627
4261
|
}
|
|
3628
4262
|
break;
|
|
@@ -3642,7 +4276,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3642
4276
|
}
|
|
3643
4277
|
return null;
|
|
3644
4278
|
};
|
|
3645
|
-
Object.
|
|
4279
|
+
Object.entries(fnMap).forEach(([name, f]) => {
|
|
3646
4280
|
traverseAst(f.ast, undefined, (node) => {
|
|
3647
4281
|
const ret = cleanup(node);
|
|
3648
4282
|
if (ret === false) {
|
|
@@ -3650,16 +4284,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3650
4284
|
}
|
|
3651
4285
|
return ret;
|
|
3652
4286
|
});
|
|
4287
|
+
if (state.config && state.config.checkBuildPragmas) {
|
|
4288
|
+
pragmaChecker(state, f.ast, state.diagnostics?.[name]);
|
|
4289
|
+
}
|
|
3653
4290
|
});
|
|
3654
4291
|
return state.diagnostics;
|
|
3655
4292
|
}
|
|
3656
4293
|
function optimizeCall(state, node, context) {
|
|
3657
4294
|
const [name, results] = state.lookupNonlocal(node.callee);
|
|
3658
|
-
const callees = results
|
|
3659
|
-
results
|
|
3660
|
-
.map((r) => r.results)
|
|
3661
|
-
.flat()
|
|
3662
|
-
.filter((c) => c.type === "FunctionDeclaration");
|
|
4295
|
+
const callees = results ? findCallees(results) : null;
|
|
3663
4296
|
if (!callees || !callees.length) {
|
|
3664
4297
|
const n = name ||
|
|
3665
4298
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -3668,14 +4301,24 @@ function optimizeCall(state, node, context) {
|
|
|
3668
4301
|
"name" in node.callee.property &&
|
|
3669
4302
|
node.callee.property.name);
|
|
3670
4303
|
if (n) {
|
|
3671
|
-
state.
|
|
4304
|
+
if (hasProperty(state.allFunctions, n)) {
|
|
4305
|
+
if (state.currentFunction) {
|
|
4306
|
+
recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
|
|
4307
|
+
}
|
|
4308
|
+
state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
|
|
4309
|
+
}
|
|
3672
4310
|
}
|
|
3673
|
-
else {
|
|
3674
|
-
//
|
|
3675
|
-
//
|
|
4311
|
+
else if (state.currentFunction) {
|
|
4312
|
+
// I don't think this can happen: foo[x](args)
|
|
4313
|
+
// doesn't parse, so you can't even do things like
|
|
4314
|
+
// $.Toybox.Lang[:format]("fmt", [])
|
|
4315
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3676
4316
|
}
|
|
3677
4317
|
return null;
|
|
3678
4318
|
}
|
|
4319
|
+
if (state.currentFunction) {
|
|
4320
|
+
recordCalledFuncs(state.currentFunction, callees);
|
|
4321
|
+
}
|
|
3679
4322
|
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
3680
4323
|
const callee = callees[0].node;
|
|
3681
4324
|
if (!context &&
|
|
@@ -3886,20 +4529,18 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
3886
4529
|
return next ? (result ? result.concat(next) : next) : result;
|
|
3887
4530
|
}, null);
|
|
3888
4531
|
};
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
return lookupInContext(ns) || false;
|
|
3902
|
-
}
|
|
4532
|
+
const ndecls = ns[decls];
|
|
4533
|
+
if (ast_hasProperty(ndecls, node.name)) {
|
|
4534
|
+
return ndecls[node.name];
|
|
4535
|
+
}
|
|
4536
|
+
switch (ns.type) {
|
|
4537
|
+
case "ClassDeclaration":
|
|
4538
|
+
if (!isStatic) {
|
|
4539
|
+
return superChain(ns) || superChainScopes(ns) || false;
|
|
4540
|
+
}
|
|
4541
|
+
// fall through
|
|
4542
|
+
case "ModuleDeclaration":
|
|
4543
|
+
return lookupInContext(ns) || false;
|
|
3903
4544
|
}
|
|
3904
4545
|
return null;
|
|
3905
4546
|
}
|
|
@@ -3922,6 +4563,13 @@ function sameLookupDefinition(a, b) {
|
|
|
3922
4563
|
function api_sameLookupResult(a, b) {
|
|
3923
4564
|
return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
|
|
3924
4565
|
}
|
|
4566
|
+
function api_isLookupCandidate(node) {
|
|
4567
|
+
return node.computed
|
|
4568
|
+
? node.property.type === "UnaryExpression" &&
|
|
4569
|
+
node.property.operator === ":" &&
|
|
4570
|
+
node.property.argument
|
|
4571
|
+
: node.property.type === "Identifier" && node.property;
|
|
4572
|
+
}
|
|
3925
4573
|
/**
|
|
3926
4574
|
*
|
|
3927
4575
|
* @param state - The ProgramState
|
|
@@ -3942,9 +4590,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3942
4590
|
const stack = maybeStack || state.stack;
|
|
3943
4591
|
switch (node.type) {
|
|
3944
4592
|
case "MemberExpression": {
|
|
3945
|
-
|
|
4593
|
+
const property = api_isLookupCandidate(node);
|
|
4594
|
+
if (!property)
|
|
3946
4595
|
break;
|
|
3947
|
-
const property = node.property;
|
|
3948
4596
|
let result;
|
|
3949
4597
|
if (node.object.type === "ThisExpression") {
|
|
3950
4598
|
[, result] = lookup(state, decls, node.property, name, stack, true);
|
|
@@ -3958,6 +4606,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3958
4606
|
result = results.reduce((current, lookupDef) => {
|
|
3959
4607
|
const items = lookupDef.results
|
|
3960
4608
|
.map((module) => {
|
|
4609
|
+
if (!api_isStateNode(module)) {
|
|
4610
|
+
return null;
|
|
4611
|
+
}
|
|
3961
4612
|
const res = checkOne(state, module, decls, property, false);
|
|
3962
4613
|
return res ? { parent: module, results: res } : null;
|
|
3963
4614
|
})
|
|
@@ -4042,6 +4693,8 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
4042
4693
|
}
|
|
4043
4694
|
function api_collectNamespaces(ast, stateIn) {
|
|
4044
4695
|
const state = (stateIn || {});
|
|
4696
|
+
if (!state.nextExposed)
|
|
4697
|
+
state.nextExposed = {};
|
|
4045
4698
|
if (!state.index)
|
|
4046
4699
|
state.index = {};
|
|
4047
4700
|
if (!state.stack) {
|
|
@@ -4077,7 +4730,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4077
4730
|
state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
|
|
4078
4731
|
? { ...elm }
|
|
4079
4732
|
: elm);
|
|
4080
|
-
state.inType =
|
|
4733
|
+
state.inType = 0;
|
|
4081
4734
|
state.traverse = (root) => ast_traverseAst(root, (node) => {
|
|
4082
4735
|
try {
|
|
4083
4736
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
@@ -4085,6 +4738,13 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4085
4738
|
return [];
|
|
4086
4739
|
}
|
|
4087
4740
|
switch (node.type) {
|
|
4741
|
+
case "UnaryExpression":
|
|
4742
|
+
if (node.operator === ":" && !state.inType) {
|
|
4743
|
+
state.nextExposed[node.argument.name] = true;
|
|
4744
|
+
}
|
|
4745
|
+
break;
|
|
4746
|
+
case "AttributeList":
|
|
4747
|
+
return [];
|
|
4088
4748
|
case "Program":
|
|
4089
4749
|
if (state.stack.length != 1) {
|
|
4090
4750
|
throw new Error("Unexpected stack length for Program node");
|
|
@@ -4093,7 +4753,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4093
4753
|
break;
|
|
4094
4754
|
case "TypeSpecList":
|
|
4095
4755
|
case "TypeSpecPart":
|
|
4096
|
-
state.inType
|
|
4756
|
+
state.inType++;
|
|
4097
4757
|
break;
|
|
4098
4758
|
case "ImportModule":
|
|
4099
4759
|
case "Using": {
|
|
@@ -4139,6 +4799,16 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4139
4799
|
});
|
|
4140
4800
|
}
|
|
4141
4801
|
break;
|
|
4802
|
+
case "ForStatement":
|
|
4803
|
+
if (node.init && node.init.type === "VariableDeclaration") {
|
|
4804
|
+
state.stack.push({
|
|
4805
|
+
type: "BlockStatement",
|
|
4806
|
+
fullName: undefined,
|
|
4807
|
+
name: undefined,
|
|
4808
|
+
node: node,
|
|
4809
|
+
});
|
|
4810
|
+
}
|
|
4811
|
+
break;
|
|
4142
4812
|
case "BlockStatement": {
|
|
4143
4813
|
const [parent] = state.stack.slice(-1);
|
|
4144
4814
|
if (parent.node === node ||
|
|
@@ -4205,15 +4875,17 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4205
4875
|
// an EnumDeclaration doesn't create a scope, but
|
|
4206
4876
|
// it does create a type (if it has a name)
|
|
4207
4877
|
case "EnumDeclaration": {
|
|
4208
|
-
if (!node.id)
|
|
4878
|
+
if (!node.id) {
|
|
4879
|
+
state.inType++;
|
|
4209
4880
|
break;
|
|
4881
|
+
}
|
|
4210
4882
|
const [parent] = state.stack.slice(-1);
|
|
4211
4883
|
const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
|
|
4212
4884
|
node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
|
|
4213
4885
|
}
|
|
4214
4886
|
// fall through
|
|
4215
4887
|
case "TypedefDeclaration": {
|
|
4216
|
-
state.inType
|
|
4888
|
+
state.inType++;
|
|
4217
4889
|
const name = node.id.name;
|
|
4218
4890
|
const [parent] = state.stack.slice(-1);
|
|
4219
4891
|
if (!parent.type_decls)
|
|
@@ -4266,13 +4938,21 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4266
4938
|
break;
|
|
4267
4939
|
}
|
|
4268
4940
|
case "EnumStringBody": {
|
|
4269
|
-
state.inType
|
|
4941
|
+
if (state.inType !== 1) {
|
|
4942
|
+
throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${state.inType}.`);
|
|
4943
|
+
}
|
|
4944
|
+
state.inType--;
|
|
4270
4945
|
const [parent] = state.stack.slice(-1);
|
|
4271
4946
|
const values = parent.decls || (parent.decls = {});
|
|
4272
4947
|
let prev = -1;
|
|
4273
4948
|
node.members.forEach((m, i) => {
|
|
4274
4949
|
if (m.type == "Identifier") {
|
|
4275
|
-
prev
|
|
4950
|
+
if (typeof prev === "bigint") {
|
|
4951
|
+
prev += 1n;
|
|
4952
|
+
}
|
|
4953
|
+
else {
|
|
4954
|
+
prev += 1;
|
|
4955
|
+
}
|
|
4276
4956
|
m = node.members[i] = {
|
|
4277
4957
|
type: "EnumStringMember",
|
|
4278
4958
|
loc: m.loc,
|
|
@@ -4282,7 +4962,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4282
4962
|
init: {
|
|
4283
4963
|
type: "Literal",
|
|
4284
4964
|
value: prev,
|
|
4285
|
-
raw: prev.toString(),
|
|
4965
|
+
raw: prev.toString() + (typeof prev === "bigint" ? "l" : ""),
|
|
4286
4966
|
enumType: m.enumType,
|
|
4287
4967
|
loc: m.loc,
|
|
4288
4968
|
start: m.start,
|
|
@@ -4334,22 +5014,22 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4334
5014
|
if (state.post)
|
|
4335
5015
|
ret = state.post(node, state);
|
|
4336
5016
|
switch (type) {
|
|
4337
|
-
|
|
4338
|
-
// generally occur in TypeSpecLists. But do clear it for
|
|
4339
|
-
// SizedArrayExpression, since thats the only place they
|
|
4340
|
-
// happen on their own.
|
|
4341
|
-
case "SizedArrayExpression":
|
|
5017
|
+
case "TypeSpecPart":
|
|
4342
5018
|
case "TypeSpecList":
|
|
4343
5019
|
case "TypedefDeclaration":
|
|
4344
5020
|
case "EnumDeclaration":
|
|
4345
|
-
state.inType
|
|
5021
|
+
state.inType--;
|
|
4346
5022
|
break;
|
|
4347
5023
|
case "EnumStringBody":
|
|
4348
|
-
state.inType
|
|
5024
|
+
state.inType++;
|
|
4349
5025
|
break;
|
|
4350
5026
|
}
|
|
4351
5027
|
const [parent] = state.stack.slice(-1);
|
|
4352
5028
|
if (parent.node === node ||
|
|
5029
|
+
// The pre function might cause node.body to be skipped,
|
|
5030
|
+
// so we need to check here, just in case.
|
|
5031
|
+
// (this actually happens with prettier-extenison-monkeyc's
|
|
5032
|
+
// findItemsByRange)
|
|
4353
5033
|
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
4354
5034
|
delete parent.usings;
|
|
4355
5035
|
delete parent.imports;
|
|
@@ -4368,6 +5048,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4368
5048
|
}
|
|
4369
5049
|
});
|
|
4370
5050
|
state.traverse(ast);
|
|
5051
|
+
if (state.inType) {
|
|
5052
|
+
throw new Error(`inType was non-zero on exit: ${state.inType}`);
|
|
5053
|
+
}
|
|
4371
5054
|
if (state.stack.length != 1) {
|
|
4372
5055
|
throw new Error("Invalid AST!");
|
|
4373
5056
|
}
|
|
@@ -4402,7 +5085,7 @@ function api_formatAst(node, monkeyCSource = null) {
|
|
|
4402
5085
|
// json. The parser knows to just treat the last line of the input
|
|
4403
5086
|
// as the ast itself, and the printers will find what they're
|
|
4404
5087
|
// looking for in the source.
|
|
4405
|
-
const source = (monkeyCSource || "") + "\n" +
|
|
5088
|
+
const source = (monkeyCSource || "") + "\n" + (0,prettier_plugin_monkeyc_namespaceObject.serializeMonkeyC)(node);
|
|
4406
5089
|
return external_prettier_namespaceObject.format(source, {
|
|
4407
5090
|
parser: "monkeyc-json",
|
|
4408
5091
|
plugins: [(prettier_plugin_monkeyc_default())],
|
|
@@ -4489,6 +5172,36 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
4489
5172
|
}
|
|
4490
5173
|
return null;
|
|
4491
5174
|
}
|
|
5175
|
+
const invokeInfo = {};
|
|
5176
|
+
const toyboxFnInfo = {};
|
|
5177
|
+
function api_getApiFunctionInfo(func) {
|
|
5178
|
+
if (func.fullName === "$.Toybox.Lang.Method.invoke" ||
|
|
5179
|
+
(func.node.params &&
|
|
5180
|
+
func.node.params.some((param) => param.type === "BinaryExpression" &&
|
|
5181
|
+
param.right.ts.some((tsp) => tsp.type === "TypeSpecPart" && tsp.callspec)))) {
|
|
5182
|
+
if (!invokeInfo.calledFuncs) {
|
|
5183
|
+
invokeInfo.modifiedDecls = new Set();
|
|
5184
|
+
invokeInfo.calledFuncs = new Set();
|
|
5185
|
+
invokeInfo.callsExposed = true;
|
|
5186
|
+
}
|
|
5187
|
+
if (func.name === "initialize") {
|
|
5188
|
+
const top = func.stack[func.stack.length - 1];
|
|
5189
|
+
if (top.type === "ClassDeclaration") {
|
|
5190
|
+
top.hasInvoke = true;
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
return invokeInfo;
|
|
5194
|
+
}
|
|
5195
|
+
if (!toyboxFnInfo.calledFuncs) {
|
|
5196
|
+
toyboxFnInfo.modifiedDecls = new Set();
|
|
5197
|
+
toyboxFnInfo.calledFuncs = new Set();
|
|
5198
|
+
toyboxFnInfo.resolvedDecls = new Set();
|
|
5199
|
+
}
|
|
5200
|
+
return toyboxFnInfo;
|
|
5201
|
+
}
|
|
5202
|
+
function api_markInvokeClassMethod(func) {
|
|
5203
|
+
func.info = invokeInfo;
|
|
5204
|
+
}
|
|
4492
5205
|
|
|
4493
5206
|
})();
|
|
4494
5207
|
|