@markw65/monkeyc-optimizer 1.0.30 → 1.0.31
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 +35 -0
- package/build/api.cjs +977 -275
- package/build/optimizer.cjs +877 -365
- package/build/src/api.d.ts +5 -1
- package/build/src/ast.d.ts +2 -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 +2 -2
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),
|
|
@@ -464,21 +467,150 @@ function ast_withLoc(node, start, end) {
|
|
|
464
467
|
}
|
|
465
468
|
return node;
|
|
466
469
|
}
|
|
467
|
-
function ast_withLocDeep(node, start, end) {
|
|
468
|
-
node = ast_withLoc({ ...node }, start, end);
|
|
470
|
+
function ast_withLocDeep(node, start, end, inplace) {
|
|
471
|
+
node = ast_withLoc(inplace ? node : { ...node }, start, end);
|
|
469
472
|
for (const key of mctreeTypeInfo[node.type].keys) {
|
|
470
473
|
const value = node[key];
|
|
471
474
|
if (!value)
|
|
472
475
|
continue;
|
|
473
|
-
const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end) : v;
|
|
476
|
+
const fix = (v) => isMCTreeNode(v) ? ast_withLocDeep(v, start, end, inplace) : v;
|
|
474
477
|
const repl = Array.isArray(value) ? value.map(fix) : fix(value);
|
|
475
|
-
node[key] = repl;
|
|
478
|
+
inplace || (node[key] = repl);
|
|
476
479
|
}
|
|
477
480
|
return node;
|
|
478
481
|
}
|
|
482
|
+
function ast_cloneDeep(node) {
|
|
483
|
+
return ast_withLocDeep(node, null);
|
|
484
|
+
}
|
|
479
485
|
|
|
480
486
|
;// CONCATENATED MODULE: external "./api.cjs"
|
|
481
487
|
const external_api_cjs_namespaceObject = require("./api.cjs");
|
|
488
|
+
;// CONCATENATED MODULE: ./src/function-info.ts
|
|
489
|
+
|
|
490
|
+
function function_info_cloneSet(ae) {
|
|
491
|
+
return new Set(ae);
|
|
492
|
+
}
|
|
493
|
+
function function_info_mergeSet(a, b) {
|
|
494
|
+
b.forEach((event) => a.add(event));
|
|
495
|
+
}
|
|
496
|
+
function recordModifiedDecl(func, decl) {
|
|
497
|
+
if (!func.next_info) {
|
|
498
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
499
|
+
}
|
|
500
|
+
func.next_info.modifiedDecls.add(decl);
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
function function_info_recordModifiedDecls(func, lookupDefs) {
|
|
504
|
+
lookupDefs.forEach((lookupDef) => lookupDef.results.forEach((result) => {
|
|
505
|
+
if (result.type == "VariableDeclarator" && result.node.kind === "var") {
|
|
506
|
+
recordModifiedDecl(func, result);
|
|
507
|
+
}
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
function function_info_recordModifiedName(func, name) {
|
|
511
|
+
if (!func.next_info) {
|
|
512
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
513
|
+
}
|
|
514
|
+
if (!func.next_info.modifiedNames) {
|
|
515
|
+
func.next_info.modifiedNames = new Set();
|
|
516
|
+
}
|
|
517
|
+
func.next_info.modifiedNames.add(name);
|
|
518
|
+
}
|
|
519
|
+
function function_info_recordModifiedUnknown(func) {
|
|
520
|
+
if (!func.next_info) {
|
|
521
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
522
|
+
}
|
|
523
|
+
func.next_info.modifiedUnknown = true;
|
|
524
|
+
}
|
|
525
|
+
function recordCalledFunc(func, callee) {
|
|
526
|
+
if (!func.next_info) {
|
|
527
|
+
func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
|
|
528
|
+
}
|
|
529
|
+
func.next_info.calledFuncs.add(callee);
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
function function_info_recordCalledFuncs(func, callees) {
|
|
533
|
+
callees.forEach((callee) => {
|
|
534
|
+
recordCalledFunc(func, callee);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
function function_info_functionMayModify(state, func, decl) {
|
|
538
|
+
const info = func.info;
|
|
539
|
+
if (!info || info.modifiedUnknown)
|
|
540
|
+
return true;
|
|
541
|
+
if (info.resolvedDecls) {
|
|
542
|
+
return info.resolvedDecls.has(decl);
|
|
543
|
+
}
|
|
544
|
+
if (info.modifiedNames?.has(decl.name))
|
|
545
|
+
return true;
|
|
546
|
+
if (info.modifiedDecls.has(decl))
|
|
547
|
+
return true;
|
|
548
|
+
const visited = new Set();
|
|
549
|
+
const resolved = new Set();
|
|
550
|
+
const resolveDecls = (f) => {
|
|
551
|
+
if (visited.has(f))
|
|
552
|
+
return true;
|
|
553
|
+
if (!f.info)
|
|
554
|
+
return false;
|
|
555
|
+
if (f.info.modifiedUnknown) {
|
|
556
|
+
info.modifiedUnknown = true;
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
if (f.info.modifiedNames) {
|
|
560
|
+
if (info.modifiedNames) {
|
|
561
|
+
function_info_mergeSet(info.modifiedNames, f.info.modifiedNames);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
info.modifiedNames = function_info_cloneSet(f.info.modifiedNames);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function_info_mergeSet(resolved, f.info.modifiedDecls);
|
|
568
|
+
visited.add(f);
|
|
569
|
+
const q = true;
|
|
570
|
+
if (q &&
|
|
571
|
+
f.info.callsExposed &&
|
|
572
|
+
state.exposed &&
|
|
573
|
+
!Object.keys(state.exposed).every((key) => !state.allFunctions[key] ||
|
|
574
|
+
state.allFunctions[key].every(resolveDecls))) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
return Array.from(f.info.calledFuncs).every(resolveDecls);
|
|
578
|
+
};
|
|
579
|
+
if (resolveDecls(func)) {
|
|
580
|
+
info.resolvedDecls = resolved;
|
|
581
|
+
return resolved.has(decl);
|
|
582
|
+
}
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
function function_info_findCallees(lookupDefs) {
|
|
586
|
+
const decls = lookupDefs.reduce((decls, r) => (decls ? decls.concat(r.results) : r.results), null);
|
|
587
|
+
return (decls &&
|
|
588
|
+
decls.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
|
|
589
|
+
}
|
|
590
|
+
function function_info_findCalleesForNew(lookupDefs) {
|
|
591
|
+
const initializer = (decl) => {
|
|
592
|
+
if (hasProperty(decl.decls, "initialize")) {
|
|
593
|
+
return decl.decls["initialize"];
|
|
594
|
+
}
|
|
595
|
+
if (decl.superClass && decl.superClass !== true) {
|
|
596
|
+
return decl.superClass.reduce((cur, cls) => {
|
|
597
|
+
const init = initializer(cls);
|
|
598
|
+
if (init) {
|
|
599
|
+
if (!cur)
|
|
600
|
+
return init;
|
|
601
|
+
return cur.concat(init);
|
|
602
|
+
}
|
|
603
|
+
return cur;
|
|
604
|
+
}, null);
|
|
605
|
+
}
|
|
606
|
+
return null;
|
|
607
|
+
};
|
|
608
|
+
return lookupDefs.flatMap((r) => r.results
|
|
609
|
+
.filter((decl) => decl.type === "ClassDeclaration")
|
|
610
|
+
.flatMap(initializer)
|
|
611
|
+
.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
|
|
612
|
+
}
|
|
613
|
+
|
|
482
614
|
;// CONCATENATED MODULE: ./src/variable-renamer.ts
|
|
483
615
|
|
|
484
616
|
|
|
@@ -539,6 +671,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
|
|
|
539
671
|
|
|
540
672
|
|
|
541
673
|
|
|
674
|
+
|
|
542
675
|
function getArgSafety(state, func, args, requireAll) {
|
|
543
676
|
// determine whether decl might be changed by a function call
|
|
544
677
|
// or assignment during the evaluation of FunctionStateNode.
|
|
@@ -571,8 +704,9 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
571
704
|
}
|
|
572
705
|
};
|
|
573
706
|
const safeArgs = [];
|
|
707
|
+
const argDecls = [];
|
|
574
708
|
let allSafe = true;
|
|
575
|
-
if (!args.every((arg) => {
|
|
709
|
+
if (!args.every((arg, i) => {
|
|
576
710
|
switch (arg.type) {
|
|
577
711
|
case "Literal":
|
|
578
712
|
safeArgs.push(true);
|
|
@@ -586,13 +720,17 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
586
720
|
safeArgs.push(null);
|
|
587
721
|
return !requireAll;
|
|
588
722
|
}
|
|
589
|
-
const
|
|
723
|
+
const decl = results[0].results[0];
|
|
724
|
+
const safety = getSafety(decl);
|
|
590
725
|
safeArgs.push(safety);
|
|
591
726
|
if (!safety) {
|
|
592
727
|
allSafe = false;
|
|
593
728
|
if (safety === null) {
|
|
594
729
|
return !requireAll;
|
|
595
730
|
}
|
|
731
|
+
else if (decl.type === "VariableDeclarator") {
|
|
732
|
+
argDecls[i] = decl;
|
|
733
|
+
}
|
|
596
734
|
}
|
|
597
735
|
return true;
|
|
598
736
|
}
|
|
@@ -605,34 +743,91 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
605
743
|
}
|
|
606
744
|
if (allSafe && requireAll)
|
|
607
745
|
return true;
|
|
608
|
-
|
|
746
|
+
const callsSeen = new Set();
|
|
747
|
+
const modifiedDecls = new Set();
|
|
748
|
+
let modifiedUnknown = false;
|
|
609
749
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
610
750
|
// look for uses of "unsafe" args that occur after a call.
|
|
611
751
|
// use post to do the checking, because arguments are evaluated
|
|
612
752
|
// prior to the call, so eg "return f(x.y);" is fine, but
|
|
613
753
|
// "return f()+x.y" is not.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
754
|
+
const { pre, post, stack } = state;
|
|
755
|
+
try {
|
|
756
|
+
delete state.pre;
|
|
757
|
+
state.post = (node) => {
|
|
758
|
+
switch (node.type) {
|
|
759
|
+
case "AssignmentExpression":
|
|
760
|
+
case "UpdateExpression": {
|
|
761
|
+
const v = node.type == "UpdateExpression" ? node.argument : node.left;
|
|
762
|
+
if (v.type === "Identifier" && hasProperty(params, v.name)) {
|
|
763
|
+
// If a parameter is modified, we can't just substitute the
|
|
764
|
+
// argument wherever the parameter is used.
|
|
765
|
+
safeArgs[params[v.name]] = null;
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
if (modifiedUnknown)
|
|
769
|
+
break;
|
|
770
|
+
const [, results] = state.lookup(v);
|
|
771
|
+
if (results) {
|
|
772
|
+
results.forEach((r) => r.results.forEach((decl) => decl.type === "VariableDeclarator" && modifiedDecls.add(decl)));
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
modifiedUnknown = true;
|
|
776
|
+
}
|
|
777
|
+
break;
|
|
621
778
|
}
|
|
779
|
+
case "CallExpression":
|
|
780
|
+
case "NewExpression":
|
|
781
|
+
if (!modifiedUnknown) {
|
|
782
|
+
const [, results] = state.lookup(node.callee, null,
|
|
783
|
+
// calls are looked up as non-locals, but new is not
|
|
784
|
+
node.type === "CallExpression" ? func.stack : state.stack);
|
|
785
|
+
if (!results) {
|
|
786
|
+
const callee_name = node.callee.type === "Identifier"
|
|
787
|
+
? node.callee
|
|
788
|
+
: node.callee.type === "MemberExpression"
|
|
789
|
+
? isLookupCandidate(node.callee)
|
|
790
|
+
: null;
|
|
791
|
+
if (callee_name) {
|
|
792
|
+
const callees = state.allFunctions[callee_name.name];
|
|
793
|
+
if (callees) {
|
|
794
|
+
callees.forEach((callee) => callsSeen.add(callee));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
modifiedUnknown = true;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
const callees = node.type === "CallExpression"
|
|
803
|
+
? findCallees(results)
|
|
804
|
+
: findCalleesForNew(results);
|
|
805
|
+
if (callees) {
|
|
806
|
+
callees.forEach((callee) => callsSeen.add(callee));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
break;
|
|
811
|
+
case "Identifier":
|
|
812
|
+
if (hasProperty(params, node.name) &&
|
|
813
|
+
!safeArgs[params[node.name]] &&
|
|
814
|
+
(modifiedUnknown ||
|
|
815
|
+
!argDecls[params[node.name]] ||
|
|
816
|
+
modifiedDecls.has(argDecls[params[node.name]]) ||
|
|
817
|
+
Array.from(callsSeen).some((callee) => functionMayModify(state, callee, argDecls[params[node.name]])))) {
|
|
818
|
+
safeArgs[params[node.name]] = null;
|
|
819
|
+
}
|
|
622
820
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
});
|
|
821
|
+
return null;
|
|
822
|
+
};
|
|
823
|
+
state.stack = func.stack;
|
|
824
|
+
state.traverse(func.node.body);
|
|
825
|
+
}
|
|
826
|
+
finally {
|
|
827
|
+
state.pre = pre;
|
|
828
|
+
state.post = post;
|
|
829
|
+
state.stack = stack;
|
|
830
|
+
}
|
|
636
831
|
return safeArgs;
|
|
637
832
|
}
|
|
638
833
|
function canInline(state, func, args) {
|
|
@@ -737,9 +932,6 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
737
932
|
state.pre = (node) => {
|
|
738
933
|
if (failed)
|
|
739
934
|
return [];
|
|
740
|
-
node.start = call.start;
|
|
741
|
-
node.end = call.end;
|
|
742
|
-
node.loc = call.loc;
|
|
743
935
|
if (replacements.has(node))
|
|
744
936
|
return false;
|
|
745
937
|
const result = pre(node, state);
|
|
@@ -754,6 +946,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
754
946
|
if (params[paramName] >= 0)
|
|
755
947
|
return null;
|
|
756
948
|
const name = renameVariable(state, locals, paramName) || paramName;
|
|
949
|
+
locals.map[name] = true;
|
|
757
950
|
return {
|
|
758
951
|
type: "VariableDeclarator",
|
|
759
952
|
id: { type: "Identifier", name },
|
|
@@ -772,31 +965,49 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
772
965
|
}
|
|
773
966
|
return result;
|
|
774
967
|
};
|
|
968
|
+
const fixId = (node) => {
|
|
969
|
+
if (state.inType)
|
|
970
|
+
return null;
|
|
971
|
+
if (hasProperty(params, node.name)) {
|
|
972
|
+
const ix = params[node.name];
|
|
973
|
+
if (ix >= 0) {
|
|
974
|
+
const replacement = { ...call.arguments[ix] };
|
|
975
|
+
replacements.add(replacement);
|
|
976
|
+
return replacement;
|
|
977
|
+
}
|
|
978
|
+
return null;
|
|
979
|
+
}
|
|
980
|
+
const replacement = fixNodeScope(state, node, func.stack);
|
|
981
|
+
if (!replacement) {
|
|
982
|
+
failed = true;
|
|
983
|
+
inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
|
|
984
|
+
}
|
|
985
|
+
return replacement;
|
|
986
|
+
};
|
|
775
987
|
state.post = (node) => {
|
|
776
988
|
if (failed)
|
|
777
989
|
return post(node, state);
|
|
778
990
|
let replacement = null;
|
|
779
991
|
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;
|
|
992
|
+
case "AssignmentExpression":
|
|
993
|
+
if (node.left.type === "Identifier") {
|
|
994
|
+
const rep = fixId(node.left);
|
|
995
|
+
if (rep) {
|
|
996
|
+
node.left = rep;
|
|
789
997
|
}
|
|
790
|
-
break;
|
|
791
998
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
999
|
+
break;
|
|
1000
|
+
case "UpdateExpression":
|
|
1001
|
+
if (node.argument.type === "Identifier") {
|
|
1002
|
+
const rep = fixId(node.argument);
|
|
1003
|
+
if (rep) {
|
|
1004
|
+
node.argument = rep;
|
|
1005
|
+
}
|
|
797
1006
|
}
|
|
798
1007
|
break;
|
|
799
|
-
|
|
1008
|
+
case "Identifier":
|
|
1009
|
+
replacement = fixId(node);
|
|
1010
|
+
break;
|
|
800
1011
|
}
|
|
801
1012
|
const ret = post(replacement || node, state);
|
|
802
1013
|
return ret === false || ret ? ret : replacement;
|
|
@@ -965,7 +1176,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
965
1176
|
}
|
|
966
1177
|
}
|
|
967
1178
|
}
|
|
968
|
-
const body =
|
|
1179
|
+
const body = cloneDeep(func.node.body);
|
|
969
1180
|
const safeArgs = getArgSafety(state, func, call.arguments, false);
|
|
970
1181
|
const params = Object.fromEntries(func.node.params.map((param, i) => {
|
|
971
1182
|
const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
|
|
@@ -1014,18 +1225,19 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
1014
1225
|
--block.body.length;
|
|
1015
1226
|
}
|
|
1016
1227
|
}
|
|
1228
|
+
withLocDeep(body, context, context, true);
|
|
1017
1229
|
return body;
|
|
1018
1230
|
}
|
|
1019
1231
|
function inliner_inlineFunction(state, func, call, context) {
|
|
1020
1232
|
if (context) {
|
|
1021
1233
|
return inlineWithArgs(state, func, call, context);
|
|
1022
1234
|
}
|
|
1023
|
-
const retArg =
|
|
1235
|
+
const retArg = cloneDeep(func.node.body.body[0].argument);
|
|
1024
1236
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
1025
1237
|
const map = fixupLocalsMap(state);
|
|
1026
1238
|
const ret = processInlineBody(state, func, call, retArg, params);
|
|
1027
1239
|
state.localsStack[state.localsStack.length - 1].map = map;
|
|
1028
|
-
return ret;
|
|
1240
|
+
return ret && withLocDeep(ret, call, call, true);
|
|
1029
1241
|
}
|
|
1030
1242
|
function applyTypeIfNeeded(node) {
|
|
1031
1243
|
if ("enumType" in node && node.enumType) {
|
|
@@ -1130,6 +1342,121 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
1130
1342
|
return null;
|
|
1131
1343
|
}
|
|
1132
1344
|
|
|
1345
|
+
;// CONCATENATED MODULE: ./src/pragma-checker.ts
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
function pragma_checker_pragmaChecker(state, ast, diagnostics) {
|
|
1350
|
+
const comments = ast.comments;
|
|
1351
|
+
if (!comments)
|
|
1352
|
+
return;
|
|
1353
|
+
diagnostics = diagnostics
|
|
1354
|
+
?.slice()
|
|
1355
|
+
.sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
|
|
1356
|
+
let diagIndex = 0;
|
|
1357
|
+
let index = -1;
|
|
1358
|
+
let comment;
|
|
1359
|
+
let matchers;
|
|
1360
|
+
const next = () => {
|
|
1361
|
+
while (++index < comments.length) {
|
|
1362
|
+
comment = comments[index];
|
|
1363
|
+
let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
|
|
1364
|
+
if (!match)
|
|
1365
|
+
continue;
|
|
1366
|
+
const kind = match[1];
|
|
1367
|
+
let str = match[2];
|
|
1368
|
+
matchers = [];
|
|
1369
|
+
while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
|
|
1370
|
+
matchers.push({ kind, quote: match[1], needle: match[2] });
|
|
1371
|
+
str = str.substring(match[0].length);
|
|
1372
|
+
if (!str.length)
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
if (!str.length)
|
|
1376
|
+
break;
|
|
1377
|
+
if (!matchers.length) {
|
|
1378
|
+
match = str.match(/^(\S+)\s+$/);
|
|
1379
|
+
if (match) {
|
|
1380
|
+
matchers.push({ kind, quote: '"', needle: match[1] });
|
|
1381
|
+
break;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
diagnostic(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
const matcher = (quote, needle, haystack) => {
|
|
1388
|
+
if (quote == '"') {
|
|
1389
|
+
return haystack.includes(needle);
|
|
1390
|
+
}
|
|
1391
|
+
const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
|
|
1392
|
+
return re.test(haystack);
|
|
1393
|
+
};
|
|
1394
|
+
next();
|
|
1395
|
+
traverseAst(ast, (node) => {
|
|
1396
|
+
if (index >= comments.length)
|
|
1397
|
+
return false;
|
|
1398
|
+
if (node.start && node.start >= (comment.end || Infinity)) {
|
|
1399
|
+
const { kind, quote, needle } = matchers.shift();
|
|
1400
|
+
if (kind === "match") {
|
|
1401
|
+
const haystack = formatAst(node).replace(/([\r\n]|\s)+/g, " ");
|
|
1402
|
+
if (!matcher(quote, needle, haystack)) {
|
|
1403
|
+
matcher(quote, needle, haystack);
|
|
1404
|
+
diagnostic(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
else if (kind === "expect") {
|
|
1408
|
+
const locCmp = (a, b) => {
|
|
1409
|
+
if (!b)
|
|
1410
|
+
return -1;
|
|
1411
|
+
if (a.start.line < b.start.line)
|
|
1412
|
+
return -1;
|
|
1413
|
+
if (a.start.line === b.start.line &&
|
|
1414
|
+
a.start.column < b.start.column) {
|
|
1415
|
+
return -1;
|
|
1416
|
+
}
|
|
1417
|
+
if (a.end.line > b.end.line)
|
|
1418
|
+
return 1;
|
|
1419
|
+
if (a.end.line === b.end.line && a.end.column >= b.end.column) {
|
|
1420
|
+
return 1;
|
|
1421
|
+
}
|
|
1422
|
+
return 0;
|
|
1423
|
+
};
|
|
1424
|
+
let found = false;
|
|
1425
|
+
if (diagnostics) {
|
|
1426
|
+
while (true) {
|
|
1427
|
+
if (diagIndex >= diagnostics.length) {
|
|
1428
|
+
diagnostics = null;
|
|
1429
|
+
break;
|
|
1430
|
+
}
|
|
1431
|
+
const diag = diagnostics[diagIndex];
|
|
1432
|
+
const cmp = locCmp(diag.loc, node.loc);
|
|
1433
|
+
if (cmp > 0) {
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
diagIndex++;
|
|
1437
|
+
if (cmp < 0)
|
|
1438
|
+
continue;
|
|
1439
|
+
if (matcher(quote, needle, diag.message)) {
|
|
1440
|
+
found = true;
|
|
1441
|
+
diag.type = "INFO";
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (!found) {
|
|
1446
|
+
diagnostic(state, comment.loc, `Missing error message '${needle}`, "ERROR");
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (matchers.length) {
|
|
1450
|
+
// if we're checking a series of nodes, we need
|
|
1451
|
+
// to skip over this one.
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
next();
|
|
1455
|
+
}
|
|
1456
|
+
return null;
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1133
1460
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
1134
1461
|
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
1135
1462
|
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
@@ -1652,6 +1979,7 @@ var priorityqueuejs = __webpack_require__(2789);
|
|
|
1652
1979
|
|
|
1653
1980
|
|
|
1654
1981
|
|
|
1982
|
+
|
|
1655
1983
|
/**
|
|
1656
1984
|
* This implements a pseudo Partial Redundancy Elimination
|
|
1657
1985
|
* pass. It isn't quite like traditional PRE because we're
|
|
@@ -1721,7 +2049,7 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1721
2049
|
return;
|
|
1722
2050
|
}
|
|
1723
2051
|
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
1724
|
-
const candidates = computeAttributes(head);
|
|
2052
|
+
const candidates = computeAttributes(state, head);
|
|
1725
2053
|
if (candidates) {
|
|
1726
2054
|
if (logging) {
|
|
1727
2055
|
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
@@ -1741,8 +2069,10 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1741
2069
|
let i = 0;
|
|
1742
2070
|
do {
|
|
1743
2071
|
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
1744
|
-
if (!identifiers.has(name))
|
|
2072
|
+
if (!identifiers.has(name)) {
|
|
2073
|
+
identifiers.add(name);
|
|
1745
2074
|
break;
|
|
2075
|
+
}
|
|
1746
2076
|
i++;
|
|
1747
2077
|
} while (true);
|
|
1748
2078
|
declMap.set(decl, name);
|
|
@@ -1833,7 +2163,7 @@ function buildPREGraph(state, func) {
|
|
|
1833
2163
|
case "ParenthesizedExpression":
|
|
1834
2164
|
break;
|
|
1835
2165
|
case "Literal":
|
|
1836
|
-
if (
|
|
2166
|
+
if (refCost(node) > LocalRefCost) {
|
|
1837
2167
|
let decl = literals.get(node.value);
|
|
1838
2168
|
if (!decl) {
|
|
1839
2169
|
decl = node;
|
|
@@ -1917,10 +2247,18 @@ function buildPREGraph(state, func) {
|
|
|
1917
2247
|
}
|
|
1918
2248
|
break;
|
|
1919
2249
|
}
|
|
1920
|
-
case "NewExpression":
|
|
1921
|
-
|
|
2250
|
+
case "NewExpression": {
|
|
2251
|
+
const [, results] = state.lookup(node.callee);
|
|
2252
|
+
const callees = results ? findCalleesForNew(results) : null;
|
|
1922
2253
|
liveDef(null, stmt);
|
|
1923
|
-
return { type: "mod", node, mayThrow };
|
|
2254
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2255
|
+
}
|
|
2256
|
+
case "CallExpression": {
|
|
2257
|
+
liveDef(null, stmt);
|
|
2258
|
+
const [, results] = state.lookup(node.callee);
|
|
2259
|
+
const callees = results ? findCallees(results) : null;
|
|
2260
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2261
|
+
}
|
|
1924
2262
|
default:
|
|
1925
2263
|
if (!isExpression(node))
|
|
1926
2264
|
break;
|
|
@@ -1936,12 +2274,6 @@ function buildPREGraph(state, func) {
|
|
|
1936
2274
|
function anticipatedDecls() {
|
|
1937
2275
|
return new Map();
|
|
1938
2276
|
}
|
|
1939
|
-
function cloneSet(ae) {
|
|
1940
|
-
return new Set(ae);
|
|
1941
|
-
}
|
|
1942
|
-
function mergeSet(a, b) {
|
|
1943
|
-
b.forEach((event) => a.add(event));
|
|
1944
|
-
}
|
|
1945
2277
|
function equalSet(a, b) {
|
|
1946
2278
|
if (a.size != b.size)
|
|
1947
2279
|
return false;
|
|
@@ -2020,6 +2352,7 @@ function refCost(node) {
|
|
|
2020
2352
|
switch (typeof node.value) {
|
|
2021
2353
|
case "string":
|
|
2022
2354
|
return 5;
|
|
2355
|
+
case "bigint":
|
|
2023
2356
|
case "number":
|
|
2024
2357
|
return 5;
|
|
2025
2358
|
case "boolean":
|
|
@@ -2081,7 +2414,7 @@ function candidateCost(candState) {
|
|
|
2081
2414
|
cost += defCost(candState.node) * boundarySize;
|
|
2082
2415
|
return cost;
|
|
2083
2416
|
}
|
|
2084
|
-
function computeAttributes(head) {
|
|
2417
|
+
function computeAttributes(state, head) {
|
|
2085
2418
|
const order = getPostOrder(head);
|
|
2086
2419
|
order.forEach((block, i) => {
|
|
2087
2420
|
block.order = i;
|
|
@@ -2190,7 +2523,9 @@ function computeAttributes(head) {
|
|
|
2190
2523
|
curState.forEach((candidates, decl) => {
|
|
2191
2524
|
if (decl.type === "VariableDeclarator" &&
|
|
2192
2525
|
decl.node.kind === "var" &&
|
|
2193
|
-
candidates.live
|
|
2526
|
+
candidates.live &&
|
|
2527
|
+
(!event.callees ||
|
|
2528
|
+
event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
|
|
2194
2529
|
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
2195
2530
|
candidates.live = false;
|
|
2196
2531
|
}
|
|
@@ -2379,20 +2714,26 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2379
2714
|
stmtStack.pop();
|
|
2380
2715
|
const events = nodeMap.get(node);
|
|
2381
2716
|
if (events) {
|
|
2382
|
-
|
|
2383
|
-
if (
|
|
2717
|
+
const ret = events.reduce((ret, event) => {
|
|
2718
|
+
if (event.type === "ref") {
|
|
2719
|
+
if (ret) {
|
|
2720
|
+
throw new Error(`ref found when there was already a replacement for this node`);
|
|
2721
|
+
}
|
|
2384
2722
|
if (node.type !== "Identifier" &&
|
|
2385
2723
|
node.type !== "MemberExpression" &&
|
|
2386
2724
|
node.type !== "Literal") {
|
|
2387
2725
|
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
2388
2726
|
}
|
|
2389
|
-
const name = declMap.get(
|
|
2727
|
+
const name = declMap.get(event.decl);
|
|
2390
2728
|
if (!name) {
|
|
2391
2729
|
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
2392
2730
|
}
|
|
2393
2731
|
return ident(name, node);
|
|
2394
2732
|
}
|
|
2395
|
-
|
|
2733
|
+
if (event.type === "def") {
|
|
2734
|
+
if (ret) {
|
|
2735
|
+
throw new Error(`def found when there was already a replacement for this node`);
|
|
2736
|
+
}
|
|
2396
2737
|
if (node.type !== "AssignmentExpression" &&
|
|
2397
2738
|
node.type !== "UpdateExpression") {
|
|
2398
2739
|
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
@@ -2400,7 +2741,7 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2400
2741
|
const target = node.type === "AssignmentExpression"
|
|
2401
2742
|
? node.left
|
|
2402
2743
|
: node.argument;
|
|
2403
|
-
const name = declMap.get(
|
|
2744
|
+
const name = declMap.get(event.decl);
|
|
2404
2745
|
if (!name) {
|
|
2405
2746
|
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
2406
2747
|
}
|
|
@@ -2419,20 +2760,24 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2419
2760
|
}
|
|
2420
2761
|
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
2421
2762
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2763
|
+
if (event.type === "mod") {
|
|
2764
|
+
if (!event.decl) {
|
|
2765
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
2766
|
+
}
|
|
2767
|
+
let pending = pendingMap.get(stmt);
|
|
2768
|
+
if (!pending) {
|
|
2769
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
2770
|
+
}
|
|
2771
|
+
pending.add(event);
|
|
2429
2772
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
pendingMap.set(stmt, (pending = new Set()));
|
|
2773
|
+
else {
|
|
2774
|
+
throw new Error(`Unexpected ${event.type} found`);
|
|
2433
2775
|
}
|
|
2434
|
-
|
|
2435
|
-
});
|
|
2776
|
+
return ret;
|
|
2777
|
+
}, null);
|
|
2778
|
+
if (ret) {
|
|
2779
|
+
return ret;
|
|
2780
|
+
}
|
|
2436
2781
|
}
|
|
2437
2782
|
const pending = pendingMap.get(node);
|
|
2438
2783
|
if (node.type === "SequenceExpression") {
|
|
@@ -2506,6 +2851,148 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2506
2851
|
});
|
|
2507
2852
|
}
|
|
2508
2853
|
|
|
2854
|
+
;// CONCATENATED MODULE: ./src/unused-exprs.ts
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
|
|
2858
|
+
function unused_exprs_cleanupUnusedVars(state, node) {
|
|
2859
|
+
const [parent] = state.stack.slice(-1);
|
|
2860
|
+
if (parent.node !== node) {
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
if (parent.type != "BlockStatement") {
|
|
2864
|
+
throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
|
|
2865
|
+
}
|
|
2866
|
+
if (parent.decls) {
|
|
2867
|
+
let toRemove = null;
|
|
2868
|
+
Object.values(parent.decls).forEach((decls) => {
|
|
2869
|
+
if (decls.length === 1 &&
|
|
2870
|
+
decls[0].type === "VariableDeclarator" &&
|
|
2871
|
+
!decls[0].used) {
|
|
2872
|
+
if (!toRemove)
|
|
2873
|
+
toRemove = {};
|
|
2874
|
+
toRemove[decls[0].name] = decls[0];
|
|
2875
|
+
}
|
|
2876
|
+
});
|
|
2877
|
+
if (toRemove) {
|
|
2878
|
+
const varDeclarations = new Map();
|
|
2879
|
+
traverseAst(node, null, (node) => {
|
|
2880
|
+
switch (node.type) {
|
|
2881
|
+
case "VariableDeclaration": {
|
|
2882
|
+
node.declarations.forEach((decl, i) => {
|
|
2883
|
+
const name = variableDeclarationName(decl.id);
|
|
2884
|
+
if (hasProperty(toRemove, name)) {
|
|
2885
|
+
const indices = varDeclarations.get(node);
|
|
2886
|
+
if (indices) {
|
|
2887
|
+
indices.push(i);
|
|
2888
|
+
}
|
|
2889
|
+
else {
|
|
2890
|
+
varDeclarations.set(node, [i]);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
});
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
case "ExpressionStatement":
|
|
2897
|
+
if (node.expression.type === "AssignmentExpression") {
|
|
2898
|
+
if (node.expression.left.type === "Identifier" &&
|
|
2899
|
+
hasProperty(toRemove, node.expression.left.name)) {
|
|
2900
|
+
return unused(node.expression.right);
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
else if (node.expression.type === "UpdateExpression" &&
|
|
2904
|
+
node.expression.argument.type === "Identifier" &&
|
|
2905
|
+
hasProperty(toRemove, node.expression.argument.name)) {
|
|
2906
|
+
return false;
|
|
2907
|
+
}
|
|
2908
|
+
break;
|
|
2909
|
+
case "SequenceExpression": {
|
|
2910
|
+
for (let i = node.expressions.length; i--;) {
|
|
2911
|
+
const expr = node.expressions[i];
|
|
2912
|
+
if (expr.type === "AssignmentExpression") {
|
|
2913
|
+
if (expr.left.type === "Identifier" &&
|
|
2914
|
+
hasProperty(toRemove, expr.left.name)) {
|
|
2915
|
+
const rep = unused(expr.right);
|
|
2916
|
+
if (!rep.length) {
|
|
2917
|
+
node.expressions.splice(i, 1);
|
|
2918
|
+
}
|
|
2919
|
+
else {
|
|
2920
|
+
// Sequence expressions can only be assignments
|
|
2921
|
+
// or update expressions. Even calls aren't allowed
|
|
2922
|
+
toRemove[expr.left.name] = null;
|
|
2923
|
+
expr.operator = "=";
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
else if (expr.type === "UpdateExpression" &&
|
|
2928
|
+
expr.argument.type === "Identifier" &&
|
|
2929
|
+
hasProperty(toRemove, expr.argument.name)) {
|
|
2930
|
+
node.expressions.splice(i, 1);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
break;
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return null;
|
|
2937
|
+
});
|
|
2938
|
+
varDeclarations.forEach((indices, decl) => {
|
|
2939
|
+
let index = -1;
|
|
2940
|
+
for (let ii = indices.length, j = decl.declarations.length; ii--;) {
|
|
2941
|
+
const i = indices[ii];
|
|
2942
|
+
const vdecl = decl.declarations[i];
|
|
2943
|
+
const name = variableDeclarationName(vdecl.id);
|
|
2944
|
+
if (hasProperty(toRemove, name)) {
|
|
2945
|
+
const rep = vdecl.init ? unused(vdecl.init) : [];
|
|
2946
|
+
if (rep.length) {
|
|
2947
|
+
if (parent.node.type === "ForStatement") {
|
|
2948
|
+
// declarations whose inits have side effects
|
|
2949
|
+
// can't be deleted from for statements.
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
if (index < 0) {
|
|
2953
|
+
index = parent.node.body.findIndex((s) => s === decl);
|
|
2954
|
+
if (index < 0) {
|
|
2955
|
+
throw new Error(`Failed to find variable declaration for ${variableDeclarationName(vdecl.id)}`);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (j > i + 1) {
|
|
2959
|
+
const tail = {
|
|
2960
|
+
...decl,
|
|
2961
|
+
declarations: decl.declarations.slice(i + 1, j),
|
|
2962
|
+
};
|
|
2963
|
+
if (decl.loc && vdecl.loc) {
|
|
2964
|
+
tail.loc = { ...decl.loc, start: vdecl.loc.end };
|
|
2965
|
+
tail.start = vdecl.end;
|
|
2966
|
+
}
|
|
2967
|
+
rep.push(tail);
|
|
2968
|
+
}
|
|
2969
|
+
if (decl.loc && vdecl.loc) {
|
|
2970
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
2971
|
+
decl.end = vdecl.start;
|
|
2972
|
+
}
|
|
2973
|
+
decl.declarations.splice(i);
|
|
2974
|
+
parent.node.body.splice(index + 1, 0, ...rep);
|
|
2975
|
+
j = i;
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2978
|
+
if (toRemove[name]) {
|
|
2979
|
+
j--;
|
|
2980
|
+
decl.declarations.splice(i, 1);
|
|
2981
|
+
if (i === j && decl.loc && vdecl.loc) {
|
|
2982
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
2983
|
+
decl.end = vdecl.start;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
else {
|
|
2987
|
+
delete vdecl.init;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2509
2996
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
2510
2997
|
|
|
2511
2998
|
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
@@ -2570,14 +3057,16 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2570
3057
|
return checkResults(state.lookup(node), node);
|
|
2571
3058
|
}
|
|
2572
3059
|
break;
|
|
2573
|
-
case "MemberExpression":
|
|
2574
|
-
|
|
2575
|
-
|
|
3060
|
+
case "MemberExpression": {
|
|
3061
|
+
const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
|
|
3062
|
+
if (property) {
|
|
3063
|
+
if (!name || property.name === name) {
|
|
2576
3064
|
return checkResults(state.lookup(node), node) || ["object"];
|
|
2577
3065
|
}
|
|
2578
3066
|
return ["object"];
|
|
2579
3067
|
}
|
|
2580
3068
|
break;
|
|
3069
|
+
}
|
|
2581
3070
|
case "MethodDefinition": {
|
|
2582
3071
|
if (!state.inType) {
|
|
2583
3072
|
throw new Error("Method definition outside of type!");
|
|
@@ -2586,7 +3075,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2586
3075
|
node.params.forEach((param) => {
|
|
2587
3076
|
if (param.type == "BinaryExpression") {
|
|
2588
3077
|
state.traverse(param.right);
|
|
2589
|
-
state.inType = true;
|
|
2590
3078
|
}
|
|
2591
3079
|
});
|
|
2592
3080
|
}
|
|
@@ -2609,6 +3097,9 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2609
3097
|
|
|
2610
3098
|
|
|
2611
3099
|
|
|
3100
|
+
|
|
3101
|
+
|
|
3102
|
+
|
|
2612
3103
|
function collectClassInfo(state) {
|
|
2613
3104
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
2614
3105
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -2668,8 +3159,7 @@ function collectClassInfo(state) {
|
|
|
2668
3159
|
c.decls &&
|
|
2669
3160
|
Object.values(c.decls).forEach((funcs) => {
|
|
2670
3161
|
funcs.forEach((f) => {
|
|
2671
|
-
if (
|
|
2672
|
-
f.type === "FunctionDeclaration" &&
|
|
3162
|
+
if (f.type === "FunctionDeclaration" &&
|
|
2673
3163
|
hasProperty(cls.decls, f.name)) {
|
|
2674
3164
|
f.node.hasOverride = true;
|
|
2675
3165
|
}
|
|
@@ -2682,6 +3172,15 @@ function collectClassInfo(state) {
|
|
|
2682
3172
|
state.allClasses.forEach((elm) => {
|
|
2683
3173
|
if (elm.superClass)
|
|
2684
3174
|
markOverrides(elm, elm.superClass);
|
|
3175
|
+
if (elm.hasInvoke && elm.decls) {
|
|
3176
|
+
Object.values(elm.decls).forEach((funcs) => {
|
|
3177
|
+
funcs.forEach((f) => {
|
|
3178
|
+
if (f.type === "FunctionDeclaration" && !f.isStatic) {
|
|
3179
|
+
markInvokeClassMethod(f);
|
|
3180
|
+
}
|
|
3181
|
+
});
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
2685
3184
|
});
|
|
2686
3185
|
}
|
|
2687
3186
|
function getFileSources(fnMap) {
|
|
@@ -2721,7 +3220,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2721
3220
|
const preState = {
|
|
2722
3221
|
fnMap,
|
|
2723
3222
|
config,
|
|
2724
|
-
allFunctions:
|
|
3223
|
+
allFunctions: {},
|
|
2725
3224
|
allClasses: [],
|
|
2726
3225
|
shouldExclude(node) {
|
|
2727
3226
|
if ("attrs" in node &&
|
|
@@ -2751,11 +3250,6 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2751
3250
|
pre(node, state) {
|
|
2752
3251
|
switch (node.type) {
|
|
2753
3252
|
case "FunctionDeclaration":
|
|
2754
|
-
if (markApi) {
|
|
2755
|
-
node.body = null;
|
|
2756
|
-
break;
|
|
2757
|
-
}
|
|
2758
|
-
// falls through
|
|
2759
3253
|
case "ModuleDeclaration":
|
|
2760
3254
|
case "ClassDeclaration": {
|
|
2761
3255
|
const [scope] = state.stack.slice(-1);
|
|
@@ -2766,7 +3260,18 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2766
3260
|
(scope.node.attrs &&
|
|
2767
3261
|
scope.node.attrs.access &&
|
|
2768
3262
|
scope.node.attrs.access.includes("static"));
|
|
2769
|
-
|
|
3263
|
+
if (markApi) {
|
|
3264
|
+
node.body = null;
|
|
3265
|
+
scope.info = getApiFunctionInfo(scope);
|
|
3266
|
+
delete scope.stack;
|
|
3267
|
+
}
|
|
3268
|
+
const allFuncs = state.allFunctions;
|
|
3269
|
+
if (!hasProperty(allFuncs, scope.name)) {
|
|
3270
|
+
allFuncs[scope.name] = [scope];
|
|
3271
|
+
}
|
|
3272
|
+
else {
|
|
3273
|
+
allFuncs[scope.name].push(scope);
|
|
3274
|
+
}
|
|
2770
3275
|
}
|
|
2771
3276
|
else if (scope.type === "ClassDeclaration") {
|
|
2772
3277
|
state.allClasses.push(scope);
|
|
@@ -2791,7 +3296,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2791
3296
|
value.hasTests = hasTests;
|
|
2792
3297
|
});
|
|
2793
3298
|
delete state.shouldExclude;
|
|
2794
|
-
delete state.
|
|
3299
|
+
delete state.pre;
|
|
2795
3300
|
collectClassInfo(state);
|
|
2796
3301
|
const diagnosticType = config?.checkInvalidSymbols !== "OFF"
|
|
2797
3302
|
? config?.checkInvalidSymbols || "WARNING"
|
|
@@ -2814,6 +3319,8 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2814
3319
|
});
|
|
2815
3320
|
});
|
|
2816
3321
|
}
|
|
3322
|
+
state.exposed = state.nextExposed;
|
|
3323
|
+
state.nextExposed = {};
|
|
2817
3324
|
return state;
|
|
2818
3325
|
}
|
|
2819
3326
|
function compareLiteralLike(a, b) {
|
|
@@ -2859,15 +3366,12 @@ function getLiteralNode(node) {
|
|
|
2859
3366
|
if (node.argument.type != "Literal")
|
|
2860
3367
|
return null;
|
|
2861
3368
|
switch (node.operator) {
|
|
2862
|
-
case "-":
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
value: -node.argument.value,
|
|
2867
|
-
raw: "-" + node.argument.value,
|
|
2868
|
-
enumType: node.enumType,
|
|
2869
|
-
};
|
|
3369
|
+
case "-": {
|
|
3370
|
+
const [arg, type] = getNodeValue(node.argument);
|
|
3371
|
+
if (type === "Number" || type === "Long") {
|
|
3372
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2870
3373
|
}
|
|
3374
|
+
}
|
|
2871
3375
|
}
|
|
2872
3376
|
}
|
|
2873
3377
|
return null;
|
|
@@ -2886,29 +3390,29 @@ function getNodeValue(node) {
|
|
|
2886
3390
|
if (node.type != "Literal") {
|
|
2887
3391
|
return [null, null];
|
|
2888
3392
|
}
|
|
2889
|
-
|
|
3393
|
+
if (node.value === null) {
|
|
3394
|
+
return [node, "Null"];
|
|
3395
|
+
}
|
|
3396
|
+
const type = typeof node.value;
|
|
2890
3397
|
if (type === "number") {
|
|
2891
|
-
const match =
|
|
3398
|
+
const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
|
|
2892
3399
|
if (match) {
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
type = "Double";
|
|
2897
|
-
}
|
|
2898
|
-
else {
|
|
2899
|
-
type = "Float";
|
|
3400
|
+
return match[2] === "l" || match[2] === "L"
|
|
3401
|
+
? [node, "Long"]
|
|
3402
|
+
: [node, "Number"];
|
|
2900
3403
|
}
|
|
3404
|
+
return [node, node.raw.endsWith("d") ? "Double" : "Float"];
|
|
2901
3405
|
}
|
|
2902
|
-
|
|
2903
|
-
|
|
3406
|
+
if (type === "bigint") {
|
|
3407
|
+
return [node, "Long"];
|
|
2904
3408
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
3409
|
+
if (type === "string") {
|
|
3410
|
+
return [node, "String"];
|
|
2907
3411
|
}
|
|
2908
|
-
|
|
2909
|
-
|
|
3412
|
+
if (type === "boolean") {
|
|
3413
|
+
return [node, "Boolean"];
|
|
2910
3414
|
}
|
|
2911
|
-
|
|
3415
|
+
throw new Error(`Literal has unknown type '${type}'`);
|
|
2912
3416
|
}
|
|
2913
3417
|
function fullTypeName(state, tsp) {
|
|
2914
3418
|
if (typeof tsp.name === "string") {
|
|
@@ -2953,6 +3457,46 @@ function isBooleanExpression(state, node) {
|
|
|
2953
3457
|
}
|
|
2954
3458
|
return false;
|
|
2955
3459
|
}
|
|
3460
|
+
function replacementLiteral(arg, value, type) {
|
|
3461
|
+
if (typeof value === "boolean") {
|
|
3462
|
+
type = "Boolean";
|
|
3463
|
+
}
|
|
3464
|
+
else if (type === "Number") {
|
|
3465
|
+
value = Number(BigInt.asIntN(32, BigInt(value)));
|
|
3466
|
+
}
|
|
3467
|
+
else if (type === "Long") {
|
|
3468
|
+
value = BigInt.asIntN(64, BigInt(value));
|
|
3469
|
+
}
|
|
3470
|
+
return {
|
|
3471
|
+
...arg,
|
|
3472
|
+
value,
|
|
3473
|
+
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
const operators = {
|
|
3477
|
+
"+": (left, right) => left + right,
|
|
3478
|
+
"-": (left, right) => left - right,
|
|
3479
|
+
"*": (left, right) => left * right,
|
|
3480
|
+
"/": (left, right) => left / right,
|
|
3481
|
+
"%": (left, right) => left % right,
|
|
3482
|
+
"&": (left, right) => left & right,
|
|
3483
|
+
"|": (left, right) => left | right,
|
|
3484
|
+
"^": (left, right) => left ^ right,
|
|
3485
|
+
"<<": (left, right) => left << (right & 127n),
|
|
3486
|
+
">>": (left, right) => left >> (right & 127n),
|
|
3487
|
+
"==": (left, right) =>
|
|
3488
|
+
// two string literals will compare unequal, becuase string
|
|
3489
|
+
// equality is object equality.
|
|
3490
|
+
typeof left === "string" ? false : left === right,
|
|
3491
|
+
"!=": (left, right) => typeof left === "string" ? true : left !== right,
|
|
3492
|
+
"<=": (left, right) => left <= right,
|
|
3493
|
+
">=": (left, right) => left >= right,
|
|
3494
|
+
"<": (left, right) => left < right,
|
|
3495
|
+
">": (left, right) => left > right,
|
|
3496
|
+
as: null,
|
|
3497
|
+
instanceof: null,
|
|
3498
|
+
has: null,
|
|
3499
|
+
};
|
|
2956
3500
|
function optimizeNode(state, node) {
|
|
2957
3501
|
switch (node.type) {
|
|
2958
3502
|
case "UnaryExpression": {
|
|
@@ -2967,29 +3511,17 @@ function optimizeNode(state, node) {
|
|
|
2967
3511
|
break;
|
|
2968
3512
|
case "-":
|
|
2969
3513
|
if (type === "Number" || type === "Long") {
|
|
2970
|
-
return
|
|
2971
|
-
...arg,
|
|
2972
|
-
value: -arg.value,
|
|
2973
|
-
raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
|
|
2974
|
-
};
|
|
3514
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2975
3515
|
}
|
|
2976
3516
|
break;
|
|
2977
3517
|
case "!":
|
|
2978
3518
|
case "~":
|
|
2979
3519
|
{
|
|
2980
|
-
let value;
|
|
2981
3520
|
if (type === "Number" || type === "Long") {
|
|
2982
|
-
|
|
3521
|
+
return replacementLiteral(arg, ~BigInt(arg.value), type);
|
|
2983
3522
|
}
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
}
|
|
2987
|
-
if (value !== undefined) {
|
|
2988
|
-
return {
|
|
2989
|
-
...arg,
|
|
2990
|
-
value,
|
|
2991
|
-
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
2992
|
-
};
|
|
3523
|
+
if (type === "Boolean" && node.operator == "!") {
|
|
3524
|
+
return replacementLiteral(arg, !arg.value, type);
|
|
2993
3525
|
}
|
|
2994
3526
|
}
|
|
2995
3527
|
break;
|
|
@@ -2997,27 +3529,6 @@ function optimizeNode(state, node) {
|
|
|
2997
3529
|
break;
|
|
2998
3530
|
}
|
|
2999
3531
|
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
3532
|
const op = operators[node.operator];
|
|
3022
3533
|
if (op) {
|
|
3023
3534
|
const [left, left_type] = getNodeValue(node.left);
|
|
@@ -3025,23 +3536,22 @@ function optimizeNode(state, node) {
|
|
|
3025
3536
|
if (!left || !right)
|
|
3026
3537
|
break;
|
|
3027
3538
|
let value = null;
|
|
3028
|
-
|
|
3029
|
-
|
|
3539
|
+
let type;
|
|
3540
|
+
if ((left_type != "Number" && left_type != "Long") ||
|
|
3541
|
+
left_type != right_type) {
|
|
3030
3542
|
if (node.operator !== "==" && node.operator !== "!=") {
|
|
3031
3543
|
break;
|
|
3032
3544
|
}
|
|
3033
3545
|
value = operators[node.operator](left.value, right.value);
|
|
3546
|
+
type = "Boolean";
|
|
3034
3547
|
}
|
|
3035
3548
|
else {
|
|
3036
|
-
|
|
3549
|
+
type = left_type;
|
|
3550
|
+
value = op(BigInt(left.value), BigInt(right.value));
|
|
3037
3551
|
}
|
|
3038
3552
|
if (value === null)
|
|
3039
3553
|
break;
|
|
3040
|
-
return
|
|
3041
|
-
...left,
|
|
3042
|
-
value,
|
|
3043
|
-
raw: value.toString() + (left_type === "Long" ? "l" : ""),
|
|
3044
|
-
};
|
|
3554
|
+
return replacementLiteral(left, value, type);
|
|
3045
3555
|
}
|
|
3046
3556
|
break;
|
|
3047
3557
|
}
|
|
@@ -3051,7 +3561,8 @@ function optimizeNode(state, node) {
|
|
|
3051
3561
|
break;
|
|
3052
3562
|
const falsy = left.value === false ||
|
|
3053
3563
|
left.value === null ||
|
|
3054
|
-
(
|
|
3564
|
+
((left_type === "Number" || left_type === "Long") &&
|
|
3565
|
+
(left.value === 0 || left.value === 0n));
|
|
3055
3566
|
if (falsy === (node.operator === "&&")) {
|
|
3056
3567
|
return left;
|
|
3057
3568
|
}
|
|
@@ -3092,9 +3603,7 @@ function evaluateFunction(state, func, args) {
|
|
|
3092
3603
|
const paramValues = args &&
|
|
3093
3604
|
Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
|
|
3094
3605
|
let ret = null;
|
|
3095
|
-
const body = args
|
|
3096
|
-
? JSON.parse(JSON.stringify(func.body))
|
|
3097
|
-
: func.body;
|
|
3606
|
+
const body = args ? cloneDeep(func.body) : func.body;
|
|
3098
3607
|
try {
|
|
3099
3608
|
traverseAst(body, (node) => {
|
|
3100
3609
|
switch (node.type) {
|
|
@@ -3145,12 +3654,10 @@ function markFunctionCalled(state, func) {
|
|
|
3145
3654
|
pushUnique(state.calledFunctions[func.id.name], func);
|
|
3146
3655
|
}
|
|
3147
3656
|
async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
3148
|
-
const state =
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
calledFunctions: {},
|
|
3153
|
-
};
|
|
3657
|
+
const state = (await analyze(fnMap, barrelList, config));
|
|
3658
|
+
state.localsStack = [{}];
|
|
3659
|
+
state.calledFunctions = {};
|
|
3660
|
+
state.usedByName = {};
|
|
3154
3661
|
const replace = (node, old) => {
|
|
3155
3662
|
if (node === false || node === null)
|
|
3156
3663
|
return node;
|
|
@@ -3239,6 +3746,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3239
3746
|
f.type == "FunctionDeclaration" &&
|
|
3240
3747
|
maybeCalled(f.node))) ||
|
|
3241
3748
|
(sc.superClass && checkInherited(sc, name))));
|
|
3749
|
+
const renamer = (idnode) => {
|
|
3750
|
+
const ident = idnode.type === "Identifier" ? idnode : idnode.left;
|
|
3751
|
+
const locals = topLocals();
|
|
3752
|
+
const { map } = locals;
|
|
3753
|
+
if (map) {
|
|
3754
|
+
const declName = ident.name;
|
|
3755
|
+
const name = renameVariable(state, locals, declName);
|
|
3756
|
+
if (name) {
|
|
3757
|
+
const [, results] = state.lookupValue(ident);
|
|
3758
|
+
if (!results) {
|
|
3759
|
+
throw new Error(`Didn't find local ${declName} which needed renaming`);
|
|
3760
|
+
}
|
|
3761
|
+
if (results.length !== 1) {
|
|
3762
|
+
throw new Error(`Lookup of local ${declName} found more than one result`);
|
|
3763
|
+
}
|
|
3764
|
+
const parent = results[0].parent;
|
|
3765
|
+
if (!parent) {
|
|
3766
|
+
throw new Error(`No parent in lookup of local ${declName}`);
|
|
3767
|
+
}
|
|
3768
|
+
const decls = parent.decls;
|
|
3769
|
+
if (!decls || !hasProperty(decls, declName)) {
|
|
3770
|
+
throw new Error(`Missing decls in lookup of local ${declName}`);
|
|
3771
|
+
}
|
|
3772
|
+
if (hasProperty(decls, name)) {
|
|
3773
|
+
throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
|
|
3774
|
+
}
|
|
3775
|
+
if (decls[declName].length === 1) {
|
|
3776
|
+
decls[name] = decls[declName];
|
|
3777
|
+
delete decls[declName];
|
|
3778
|
+
}
|
|
3779
|
+
else {
|
|
3780
|
+
let i = decls[declName].length;
|
|
3781
|
+
while (i--) {
|
|
3782
|
+
const decl = decls[declName][i];
|
|
3783
|
+
if (decl === idnode ||
|
|
3784
|
+
(decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
|
|
3785
|
+
decls[declName].splice(i, 1);
|
|
3786
|
+
decls[name] = [decl];
|
|
3787
|
+
break;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
if (i < 0) {
|
|
3791
|
+
throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
ident.name = name;
|
|
3795
|
+
}
|
|
3796
|
+
else {
|
|
3797
|
+
map[declName] = true;
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
};
|
|
3242
3801
|
state.pre = (node) => {
|
|
3243
3802
|
switch (node.type) {
|
|
3244
3803
|
case "ConditionalExpression":
|
|
@@ -3259,7 +3818,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3259
3818
|
result = !!value.value;
|
|
3260
3819
|
}
|
|
3261
3820
|
if (result !== null) {
|
|
3262
|
-
node.test = {
|
|
3821
|
+
node.test = {
|
|
3822
|
+
type: "Literal",
|
|
3823
|
+
value: result,
|
|
3824
|
+
raw: result.toString(),
|
|
3825
|
+
};
|
|
3263
3826
|
if (node.type === "IfStatement" ||
|
|
3264
3827
|
node.type === "ConditionalExpression") {
|
|
3265
3828
|
return [result ? "consequent" : "alternate"];
|
|
@@ -3278,7 +3841,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3278
3841
|
return null;
|
|
3279
3842
|
}
|
|
3280
3843
|
case "EnumDeclaration":
|
|
3281
|
-
return
|
|
3844
|
+
return [];
|
|
3282
3845
|
case "ForStatement": {
|
|
3283
3846
|
const map = topLocals().map;
|
|
3284
3847
|
if (map) {
|
|
@@ -3287,43 +3850,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3287
3850
|
break;
|
|
3288
3851
|
}
|
|
3289
3852
|
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
|
-
}
|
|
3853
|
+
renamer(node.id);
|
|
3307
3854
|
return ["init"];
|
|
3308
3855
|
}
|
|
3309
3856
|
case "CatchClause":
|
|
3310
3857
|
if (node.param) {
|
|
3311
3858
|
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
|
-
}
|
|
3859
|
+
renamer(node.param);
|
|
3327
3860
|
return ["body"];
|
|
3328
3861
|
}
|
|
3329
3862
|
break;
|
|
@@ -3339,14 +3872,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3339
3872
|
break;
|
|
3340
3873
|
case "UnaryExpression":
|
|
3341
3874
|
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...
|
|
3875
|
+
// node.argument is not a normal identifier.
|
|
3876
|
+
// don't visit it.
|
|
3350
3877
|
return [];
|
|
3351
3878
|
}
|
|
3352
3879
|
break;
|
|
@@ -3358,29 +3885,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3358
3885
|
if (typeof name === "string") {
|
|
3359
3886
|
node.name = name;
|
|
3360
3887
|
}
|
|
3888
|
+
const [, results] = state.lookupValue(node);
|
|
3889
|
+
if (results) {
|
|
3890
|
+
if (results.length !== 1 || results[0].results.length !== 1) {
|
|
3891
|
+
throw new Error(`Local ${node.name} had multiple lookup results`);
|
|
3892
|
+
}
|
|
3893
|
+
const parent = results[0].parent;
|
|
3894
|
+
if (!parent) {
|
|
3895
|
+
throw new Error(`Local ${node.name} had no parent`);
|
|
3896
|
+
}
|
|
3897
|
+
const decl = results[0].results[0];
|
|
3898
|
+
if (parent.type === "FunctionDeclaration" ||
|
|
3899
|
+
decl.type !== "VariableDeclarator") {
|
|
3900
|
+
// we can't optimize away function or catch parameters
|
|
3901
|
+
return [];
|
|
3902
|
+
}
|
|
3903
|
+
if (parent.type !== "BlockStatement") {
|
|
3904
|
+
throw new Error(`Local ${node.name} was not declared at block scope(??)`);
|
|
3905
|
+
}
|
|
3906
|
+
decl.used = true;
|
|
3907
|
+
}
|
|
3361
3908
|
}
|
|
3362
3909
|
}
|
|
3363
3910
|
if (hasProperty(state.index, node.name)) {
|
|
3364
3911
|
if (!lookupAndReplace(node)) {
|
|
3365
|
-
state.
|
|
3912
|
+
state.usedByName[node.name] = true;
|
|
3366
3913
|
}
|
|
3367
3914
|
}
|
|
3368
3915
|
return [];
|
|
3369
3916
|
}
|
|
3370
|
-
case "MemberExpression":
|
|
3371
|
-
|
|
3372
|
-
|
|
3917
|
+
case "MemberExpression": {
|
|
3918
|
+
const property = isLookupCandidate(node);
|
|
3919
|
+
if (property) {
|
|
3920
|
+
if (hasProperty(state.index, property.name)) {
|
|
3373
3921
|
if (lookupAndReplace(node)) {
|
|
3374
3922
|
return false;
|
|
3375
3923
|
}
|
|
3376
3924
|
else {
|
|
3377
|
-
state.
|
|
3925
|
+
state.usedByName[property.name] = true;
|
|
3378
3926
|
}
|
|
3379
3927
|
}
|
|
3380
3928
|
// Don't optimize the property.
|
|
3381
3929
|
return ["object"];
|
|
3382
3930
|
}
|
|
3383
3931
|
break;
|
|
3932
|
+
}
|
|
3933
|
+
case "AssignmentExpression":
|
|
3934
|
+
case "UpdateExpression": {
|
|
3935
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
3936
|
+
if (lhs.type === "Identifier") {
|
|
3937
|
+
const map = topLocals().map;
|
|
3938
|
+
if (map) {
|
|
3939
|
+
if (hasProperty(map, lhs.name)) {
|
|
3940
|
+
const name = map[lhs.name];
|
|
3941
|
+
if (typeof name === "string") {
|
|
3942
|
+
lhs.name = name;
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
else if (lhs.type === "MemberExpression") {
|
|
3948
|
+
state.traverse(lhs.object);
|
|
3949
|
+
if (lhs.computed) {
|
|
3950
|
+
state.traverse(lhs.property);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
return node.type === "AssignmentExpression" ? ["right"] : [];
|
|
3954
|
+
}
|
|
3384
3955
|
case "BlockStatement": {
|
|
3385
3956
|
const map = topLocals().map;
|
|
3386
3957
|
if (map) {
|
|
@@ -3396,7 +3967,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3396
3967
|
node.params &&
|
|
3397
3968
|
node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
|
|
3398
3969
|
state.localsStack.push({ node, map });
|
|
3399
|
-
const [parent] = state.stack.slice(-2);
|
|
3970
|
+
const [parent, self] = state.stack.slice(-2);
|
|
3971
|
+
if (state.currentFunction) {
|
|
3972
|
+
throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
|
|
3973
|
+
}
|
|
3974
|
+
state.currentFunction = self;
|
|
3400
3975
|
if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
|
|
3401
3976
|
let used = false;
|
|
3402
3977
|
if (node.id.name == "initialize") {
|
|
@@ -3423,10 +3998,23 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3423
3998
|
return replace(opt, node);
|
|
3424
3999
|
}
|
|
3425
4000
|
switch (node.type) {
|
|
4001
|
+
case "FunctionDeclaration":
|
|
4002
|
+
if (!state.currentFunction) {
|
|
4003
|
+
throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
|
|
4004
|
+
}
|
|
4005
|
+
state.currentFunction.info = state.currentFunction.next_info;
|
|
4006
|
+
delete state.currentFunction.next_info;
|
|
4007
|
+
delete state.currentFunction;
|
|
4008
|
+
break;
|
|
3426
4009
|
case "BlockStatement":
|
|
3427
4010
|
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
3428
4011
|
node.body.splice(0, 1, ...node.body[0].body);
|
|
3429
4012
|
}
|
|
4013
|
+
// fall through
|
|
4014
|
+
case "ForStatement":
|
|
4015
|
+
if (locals.map) {
|
|
4016
|
+
cleanupUnusedVars(state, node);
|
|
4017
|
+
}
|
|
3430
4018
|
break;
|
|
3431
4019
|
case "ConditionalExpression":
|
|
3432
4020
|
case "IfStatement":
|
|
@@ -3459,17 +4047,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3459
4047
|
return replace(optimizeCall(state, node.argument, node), node.argument);
|
|
3460
4048
|
}
|
|
3461
4049
|
break;
|
|
4050
|
+
case "NewExpression":
|
|
4051
|
+
if (state.currentFunction) {
|
|
4052
|
+
const [, results] = state.lookup(node.callee);
|
|
4053
|
+
if (results) {
|
|
4054
|
+
recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
|
|
4055
|
+
}
|
|
4056
|
+
else {
|
|
4057
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
break;
|
|
3462
4061
|
case "CallExpression": {
|
|
3463
4062
|
return replace(optimizeCall(state, node, null), node);
|
|
3464
4063
|
}
|
|
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
4064
|
case "VariableDeclaration": {
|
|
3474
4065
|
const locals = topLocals();
|
|
3475
4066
|
if (locals.map &&
|
|
@@ -3543,6 +4134,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3543
4134
|
}
|
|
3544
4135
|
}
|
|
3545
4136
|
break;
|
|
4137
|
+
case "AssignmentExpression":
|
|
4138
|
+
if (node.operator === "=" &&
|
|
4139
|
+
node.left.type === "Identifier" &&
|
|
4140
|
+
node.right.type === "Identifier" &&
|
|
4141
|
+
node.left.name === node.right.name) {
|
|
4142
|
+
return { type: "Literal", value: null, raw: "null" };
|
|
4143
|
+
}
|
|
4144
|
+
// fall through;
|
|
4145
|
+
case "UpdateExpression":
|
|
4146
|
+
if (state.currentFunction) {
|
|
4147
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
4148
|
+
const [, results] = state.lookup(lhs);
|
|
4149
|
+
if (results) {
|
|
4150
|
+
recordModifiedDecls(state.currentFunction, results);
|
|
4151
|
+
}
|
|
4152
|
+
else {
|
|
4153
|
+
const id = lhs.type === "Identifier" ? lhs : isLookupCandidate(lhs);
|
|
4154
|
+
if (id) {
|
|
4155
|
+
recordModifiedName(state.currentFunction, id.name);
|
|
4156
|
+
}
|
|
4157
|
+
else {
|
|
4158
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
break;
|
|
3546
4163
|
}
|
|
3547
4164
|
return null;
|
|
3548
4165
|
};
|
|
@@ -3550,13 +4167,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3550
4167
|
collectNamespaces(f.ast, state);
|
|
3551
4168
|
});
|
|
3552
4169
|
state.calledFunctions = {};
|
|
3553
|
-
state.exposed =
|
|
4170
|
+
state.exposed = state.nextExposed;
|
|
4171
|
+
state.nextExposed = {};
|
|
3554
4172
|
Object.values(fnMap).forEach((f) => {
|
|
3555
4173
|
collectNamespaces(f.ast, state);
|
|
3556
4174
|
});
|
|
4175
|
+
state.exposed = state.nextExposed;
|
|
4176
|
+
state.nextExposed = {};
|
|
3557
4177
|
delete state.pre;
|
|
3558
4178
|
delete state.post;
|
|
3559
|
-
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
4179
|
+
Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
|
|
3560
4180
|
const cleanup = (node) => {
|
|
3561
4181
|
switch (node.type) {
|
|
3562
4182
|
case "ThisExpression":
|
|
@@ -3566,7 +4186,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3566
4186
|
if (node.members.every((m) => {
|
|
3567
4187
|
const name = "name" in m ? m.name : m.id.name;
|
|
3568
4188
|
return (hasProperty(state.index, name) &&
|
|
3569
|
-
!hasProperty(state.exposed, name)
|
|
4189
|
+
!hasProperty(state.exposed, name) &&
|
|
4190
|
+
!hasProperty(state.usedByName, name));
|
|
3570
4191
|
})) {
|
|
3571
4192
|
node.enumType = [
|
|
3572
4193
|
...new Set(node.members.map((m) => {
|
|
@@ -3609,7 +4230,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3609
4230
|
case "VariableDeclaration": {
|
|
3610
4231
|
node.declarations = node.declarations.filter((d) => {
|
|
3611
4232
|
const name = variableDeclarationName(d.id);
|
|
3612
|
-
return (!hasProperty(state.index, name) ||
|
|
4233
|
+
return (!hasProperty(state.index, name) ||
|
|
4234
|
+
hasProperty(state.exposed, name) ||
|
|
4235
|
+
hasProperty(state.usedByName, name));
|
|
3613
4236
|
});
|
|
3614
4237
|
if (!node.declarations.length) {
|
|
3615
4238
|
return false;
|
|
@@ -3642,7 +4265,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3642
4265
|
}
|
|
3643
4266
|
return null;
|
|
3644
4267
|
};
|
|
3645
|
-
Object.
|
|
4268
|
+
Object.entries(fnMap).forEach(([name, f]) => {
|
|
3646
4269
|
traverseAst(f.ast, undefined, (node) => {
|
|
3647
4270
|
const ret = cleanup(node);
|
|
3648
4271
|
if (ret === false) {
|
|
@@ -3650,16 +4273,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3650
4273
|
}
|
|
3651
4274
|
return ret;
|
|
3652
4275
|
});
|
|
4276
|
+
if (state.config && state.config.checkBuildPragmas) {
|
|
4277
|
+
pragmaChecker(state, f.ast, state.diagnostics?.[name]);
|
|
4278
|
+
}
|
|
3653
4279
|
});
|
|
3654
4280
|
return state.diagnostics;
|
|
3655
4281
|
}
|
|
3656
4282
|
function optimizeCall(state, node, context) {
|
|
3657
4283
|
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");
|
|
4284
|
+
const callees = results ? findCallees(results) : null;
|
|
3663
4285
|
if (!callees || !callees.length) {
|
|
3664
4286
|
const n = name ||
|
|
3665
4287
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -3668,14 +4290,24 @@ function optimizeCall(state, node, context) {
|
|
|
3668
4290
|
"name" in node.callee.property &&
|
|
3669
4291
|
node.callee.property.name);
|
|
3670
4292
|
if (n) {
|
|
3671
|
-
state.
|
|
4293
|
+
if (hasProperty(state.allFunctions, n)) {
|
|
4294
|
+
if (state.currentFunction) {
|
|
4295
|
+
recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
|
|
4296
|
+
}
|
|
4297
|
+
state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
|
|
4298
|
+
}
|
|
3672
4299
|
}
|
|
3673
|
-
else {
|
|
3674
|
-
//
|
|
3675
|
-
//
|
|
4300
|
+
else if (state.currentFunction) {
|
|
4301
|
+
// I don't think this can happen: foo[x](args)
|
|
4302
|
+
// doesn't parse, so you can't even do things like
|
|
4303
|
+
// $.Toybox.Lang[:format]("fmt", [])
|
|
4304
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3676
4305
|
}
|
|
3677
4306
|
return null;
|
|
3678
4307
|
}
|
|
4308
|
+
if (state.currentFunction) {
|
|
4309
|
+
recordCalledFuncs(state.currentFunction, callees);
|
|
4310
|
+
}
|
|
3679
4311
|
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
3680
4312
|
const callee = callees[0].node;
|
|
3681
4313
|
if (!context &&
|
|
@@ -3886,20 +4518,18 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
3886
4518
|
return next ? (result ? result.concat(next) : next) : result;
|
|
3887
4519
|
}, null);
|
|
3888
4520
|
};
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
return lookupInContext(ns) || false;
|
|
3902
|
-
}
|
|
4521
|
+
const ndecls = ns[decls];
|
|
4522
|
+
if (ast_hasProperty(ndecls, node.name)) {
|
|
4523
|
+
return ndecls[node.name];
|
|
4524
|
+
}
|
|
4525
|
+
switch (ns.type) {
|
|
4526
|
+
case "ClassDeclaration":
|
|
4527
|
+
if (!isStatic) {
|
|
4528
|
+
return superChain(ns) || superChainScopes(ns) || false;
|
|
4529
|
+
}
|
|
4530
|
+
// fall through
|
|
4531
|
+
case "ModuleDeclaration":
|
|
4532
|
+
return lookupInContext(ns) || false;
|
|
3903
4533
|
}
|
|
3904
4534
|
return null;
|
|
3905
4535
|
}
|
|
@@ -3922,6 +4552,13 @@ function sameLookupDefinition(a, b) {
|
|
|
3922
4552
|
function api_sameLookupResult(a, b) {
|
|
3923
4553
|
return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
|
|
3924
4554
|
}
|
|
4555
|
+
function api_isLookupCandidate(node) {
|
|
4556
|
+
return node.computed
|
|
4557
|
+
? node.property.type === "UnaryExpression" &&
|
|
4558
|
+
node.property.operator === ":" &&
|
|
4559
|
+
node.property.argument
|
|
4560
|
+
: node.property.type === "Identifier" && node.property;
|
|
4561
|
+
}
|
|
3925
4562
|
/**
|
|
3926
4563
|
*
|
|
3927
4564
|
* @param state - The ProgramState
|
|
@@ -3942,9 +4579,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3942
4579
|
const stack = maybeStack || state.stack;
|
|
3943
4580
|
switch (node.type) {
|
|
3944
4581
|
case "MemberExpression": {
|
|
3945
|
-
|
|
4582
|
+
const property = api_isLookupCandidate(node);
|
|
4583
|
+
if (!property)
|
|
3946
4584
|
break;
|
|
3947
|
-
const property = node.property;
|
|
3948
4585
|
let result;
|
|
3949
4586
|
if (node.object.type === "ThisExpression") {
|
|
3950
4587
|
[, result] = lookup(state, decls, node.property, name, stack, true);
|
|
@@ -3958,6 +4595,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3958
4595
|
result = results.reduce((current, lookupDef) => {
|
|
3959
4596
|
const items = lookupDef.results
|
|
3960
4597
|
.map((module) => {
|
|
4598
|
+
if (!api_isStateNode(module)) {
|
|
4599
|
+
return null;
|
|
4600
|
+
}
|
|
3961
4601
|
const res = checkOne(state, module, decls, property, false);
|
|
3962
4602
|
return res ? { parent: module, results: res } : null;
|
|
3963
4603
|
})
|
|
@@ -4042,6 +4682,8 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
4042
4682
|
}
|
|
4043
4683
|
function api_collectNamespaces(ast, stateIn) {
|
|
4044
4684
|
const state = (stateIn || {});
|
|
4685
|
+
if (!state.nextExposed)
|
|
4686
|
+
state.nextExposed = {};
|
|
4045
4687
|
if (!state.index)
|
|
4046
4688
|
state.index = {};
|
|
4047
4689
|
if (!state.stack) {
|
|
@@ -4077,7 +4719,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4077
4719
|
state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
|
|
4078
4720
|
? { ...elm }
|
|
4079
4721
|
: elm);
|
|
4080
|
-
state.inType =
|
|
4722
|
+
state.inType = 0;
|
|
4081
4723
|
state.traverse = (root) => ast_traverseAst(root, (node) => {
|
|
4082
4724
|
try {
|
|
4083
4725
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
@@ -4085,6 +4727,13 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4085
4727
|
return [];
|
|
4086
4728
|
}
|
|
4087
4729
|
switch (node.type) {
|
|
4730
|
+
case "UnaryExpression":
|
|
4731
|
+
if (node.operator === ":" && !state.inType) {
|
|
4732
|
+
state.nextExposed[node.argument.name] = true;
|
|
4733
|
+
}
|
|
4734
|
+
break;
|
|
4735
|
+
case "AttributeList":
|
|
4736
|
+
return [];
|
|
4088
4737
|
case "Program":
|
|
4089
4738
|
if (state.stack.length != 1) {
|
|
4090
4739
|
throw new Error("Unexpected stack length for Program node");
|
|
@@ -4093,7 +4742,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4093
4742
|
break;
|
|
4094
4743
|
case "TypeSpecList":
|
|
4095
4744
|
case "TypeSpecPart":
|
|
4096
|
-
state.inType
|
|
4745
|
+
state.inType++;
|
|
4097
4746
|
break;
|
|
4098
4747
|
case "ImportModule":
|
|
4099
4748
|
case "Using": {
|
|
@@ -4139,6 +4788,16 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4139
4788
|
});
|
|
4140
4789
|
}
|
|
4141
4790
|
break;
|
|
4791
|
+
case "ForStatement":
|
|
4792
|
+
if (node.init && node.init.type === "VariableDeclaration") {
|
|
4793
|
+
state.stack.push({
|
|
4794
|
+
type: "BlockStatement",
|
|
4795
|
+
fullName: undefined,
|
|
4796
|
+
name: undefined,
|
|
4797
|
+
node: node,
|
|
4798
|
+
});
|
|
4799
|
+
}
|
|
4800
|
+
break;
|
|
4142
4801
|
case "BlockStatement": {
|
|
4143
4802
|
const [parent] = state.stack.slice(-1);
|
|
4144
4803
|
if (parent.node === node ||
|
|
@@ -4205,15 +4864,17 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4205
4864
|
// an EnumDeclaration doesn't create a scope, but
|
|
4206
4865
|
// it does create a type (if it has a name)
|
|
4207
4866
|
case "EnumDeclaration": {
|
|
4208
|
-
if (!node.id)
|
|
4867
|
+
if (!node.id) {
|
|
4868
|
+
state.inType++;
|
|
4209
4869
|
break;
|
|
4870
|
+
}
|
|
4210
4871
|
const [parent] = state.stack.slice(-1);
|
|
4211
4872
|
const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
|
|
4212
4873
|
node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
|
|
4213
4874
|
}
|
|
4214
4875
|
// fall through
|
|
4215
4876
|
case "TypedefDeclaration": {
|
|
4216
|
-
state.inType
|
|
4877
|
+
state.inType++;
|
|
4217
4878
|
const name = node.id.name;
|
|
4218
4879
|
const [parent] = state.stack.slice(-1);
|
|
4219
4880
|
if (!parent.type_decls)
|
|
@@ -4266,13 +4927,21 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4266
4927
|
break;
|
|
4267
4928
|
}
|
|
4268
4929
|
case "EnumStringBody": {
|
|
4269
|
-
state.inType
|
|
4930
|
+
if (state.inType !== 1) {
|
|
4931
|
+
throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${state.inType}.`);
|
|
4932
|
+
}
|
|
4933
|
+
state.inType--;
|
|
4270
4934
|
const [parent] = state.stack.slice(-1);
|
|
4271
4935
|
const values = parent.decls || (parent.decls = {});
|
|
4272
4936
|
let prev = -1;
|
|
4273
4937
|
node.members.forEach((m, i) => {
|
|
4274
4938
|
if (m.type == "Identifier") {
|
|
4275
|
-
prev
|
|
4939
|
+
if (typeof prev === "bigint") {
|
|
4940
|
+
prev += 1n;
|
|
4941
|
+
}
|
|
4942
|
+
else {
|
|
4943
|
+
prev += 1;
|
|
4944
|
+
}
|
|
4276
4945
|
m = node.members[i] = {
|
|
4277
4946
|
type: "EnumStringMember",
|
|
4278
4947
|
loc: m.loc,
|
|
@@ -4282,7 +4951,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4282
4951
|
init: {
|
|
4283
4952
|
type: "Literal",
|
|
4284
4953
|
value: prev,
|
|
4285
|
-
raw: prev.toString(),
|
|
4954
|
+
raw: prev.toString() + (typeof prev === "bigint" ? "l" : ""),
|
|
4286
4955
|
enumType: m.enumType,
|
|
4287
4956
|
loc: m.loc,
|
|
4288
4957
|
start: m.start,
|
|
@@ -4334,22 +5003,22 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4334
5003
|
if (state.post)
|
|
4335
5004
|
ret = state.post(node, state);
|
|
4336
5005
|
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":
|
|
5006
|
+
case "TypeSpecPart":
|
|
4342
5007
|
case "TypeSpecList":
|
|
4343
5008
|
case "TypedefDeclaration":
|
|
4344
5009
|
case "EnumDeclaration":
|
|
4345
|
-
state.inType
|
|
5010
|
+
state.inType--;
|
|
4346
5011
|
break;
|
|
4347
5012
|
case "EnumStringBody":
|
|
4348
|
-
state.inType
|
|
5013
|
+
state.inType++;
|
|
4349
5014
|
break;
|
|
4350
5015
|
}
|
|
4351
5016
|
const [parent] = state.stack.slice(-1);
|
|
4352
5017
|
if (parent.node === node ||
|
|
5018
|
+
// The pre function might cause node.body to be skipped,
|
|
5019
|
+
// so we need to check here, just in case.
|
|
5020
|
+
// (this actually happens with prettier-extenison-monkeyc's
|
|
5021
|
+
// findItemsByRange)
|
|
4353
5022
|
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
4354
5023
|
delete parent.usings;
|
|
4355
5024
|
delete parent.imports;
|
|
@@ -4368,6 +5037,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4368
5037
|
}
|
|
4369
5038
|
});
|
|
4370
5039
|
state.traverse(ast);
|
|
5040
|
+
if (state.inType) {
|
|
5041
|
+
throw new Error(`inType was non-zero on exit: ${state.inType}`);
|
|
5042
|
+
}
|
|
4371
5043
|
if (state.stack.length != 1) {
|
|
4372
5044
|
throw new Error("Invalid AST!");
|
|
4373
5045
|
}
|
|
@@ -4402,7 +5074,7 @@ function api_formatAst(node, monkeyCSource = null) {
|
|
|
4402
5074
|
// json. The parser knows to just treat the last line of the input
|
|
4403
5075
|
// as the ast itself, and the printers will find what they're
|
|
4404
5076
|
// looking for in the source.
|
|
4405
|
-
const source = (monkeyCSource || "") + "\n" +
|
|
5077
|
+
const source = (monkeyCSource || "") + "\n" + (0,prettier_plugin_monkeyc_namespaceObject.serializeMonkeyC)(node);
|
|
4406
5078
|
return external_prettier_namespaceObject.format(source, {
|
|
4407
5079
|
parser: "monkeyc-json",
|
|
4408
5080
|
plugins: [(prettier_plugin_monkeyc_default())],
|
|
@@ -4489,6 +5161,36 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
4489
5161
|
}
|
|
4490
5162
|
return null;
|
|
4491
5163
|
}
|
|
5164
|
+
const invokeInfo = {};
|
|
5165
|
+
const toyboxFnInfo = {};
|
|
5166
|
+
function api_getApiFunctionInfo(func) {
|
|
5167
|
+
if (func.fullName === "$.Toybox.Lang.Method.invoke" ||
|
|
5168
|
+
(func.node.params &&
|
|
5169
|
+
func.node.params.some((param) => param.type === "BinaryExpression" &&
|
|
5170
|
+
param.right.ts.some((tsp) => tsp.type === "TypeSpecPart" && tsp.callspec)))) {
|
|
5171
|
+
if (!invokeInfo.calledFuncs) {
|
|
5172
|
+
invokeInfo.modifiedDecls = new Set();
|
|
5173
|
+
invokeInfo.calledFuncs = new Set();
|
|
5174
|
+
invokeInfo.callsExposed = true;
|
|
5175
|
+
}
|
|
5176
|
+
if (func.name === "initialize") {
|
|
5177
|
+
const top = func.stack[func.stack.length - 1];
|
|
5178
|
+
if (top.type === "ClassDeclaration") {
|
|
5179
|
+
top.hasInvoke = true;
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
return invokeInfo;
|
|
5183
|
+
}
|
|
5184
|
+
if (!toyboxFnInfo.calledFuncs) {
|
|
5185
|
+
toyboxFnInfo.modifiedDecls = new Set();
|
|
5186
|
+
toyboxFnInfo.calledFuncs = new Set();
|
|
5187
|
+
toyboxFnInfo.resolvedDecls = new Set();
|
|
5188
|
+
}
|
|
5189
|
+
return toyboxFnInfo;
|
|
5190
|
+
}
|
|
5191
|
+
function api_markInvokeClassMethod(func) {
|
|
5192
|
+
func.info = invokeInfo;
|
|
5193
|
+
}
|
|
4492
5194
|
|
|
4493
5195
|
})();
|
|
4494
5196
|
|