@markw65/monkeyc-optimizer 1.0.29 → 1.0.32
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 +54 -0
- package/build/api.cjs +1049 -305
- package/build/optimizer.cjs +947 -393
- 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 +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),
|
|
@@ -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;
|
|
@@ -928,6 +1139,10 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
928
1139
|
if (!func.node || !func.node.body) {
|
|
929
1140
|
return null;
|
|
930
1141
|
}
|
|
1142
|
+
const lastStmt = (block) => {
|
|
1143
|
+
const last = block.body.slice(-1)[0];
|
|
1144
|
+
return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
|
|
1145
|
+
};
|
|
931
1146
|
let retStmtCount = 0;
|
|
932
1147
|
if (context.type === "ReturnStatement") {
|
|
933
1148
|
const last = func.node.body.body.slice(-1)[0];
|
|
@@ -950,7 +1165,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
950
1165
|
return null;
|
|
951
1166
|
}
|
|
952
1167
|
if (retStmtCount === 1) {
|
|
953
|
-
const last = func.node.body
|
|
1168
|
+
const [last] = lastStmt(func.node.body);
|
|
954
1169
|
if (!last ||
|
|
955
1170
|
last.type !== "ReturnStatement" ||
|
|
956
1171
|
((context.type === "AssignmentExpression" ||
|
|
@@ -961,7 +1176,7 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
961
1176
|
}
|
|
962
1177
|
}
|
|
963
1178
|
}
|
|
964
|
-
const body =
|
|
1179
|
+
const body = cloneDeep(func.node.body);
|
|
965
1180
|
const safeArgs = getArgSafety(state, func, call.arguments, false);
|
|
966
1181
|
const params = Object.fromEntries(func.node.params.map((param, i) => {
|
|
967
1182
|
const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
|
|
@@ -975,21 +1190,21 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
975
1190
|
}
|
|
976
1191
|
inliner_diagnostic(state, call.loc, null);
|
|
977
1192
|
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
978
|
-
const last = body
|
|
1193
|
+
const [last, block] = lastStmt(body);
|
|
979
1194
|
if (last.type != "ReturnStatement") {
|
|
980
1195
|
throw new Error("ReturnStatement got lost!");
|
|
981
1196
|
}
|
|
982
1197
|
if (last.argument) {
|
|
983
1198
|
if (context.type === "AssignmentExpression") {
|
|
984
1199
|
context.right = last.argument;
|
|
985
|
-
|
|
1200
|
+
block.body[block.body.length - 1] = {
|
|
986
1201
|
type: "ExpressionStatement",
|
|
987
1202
|
expression: context,
|
|
988
1203
|
};
|
|
989
1204
|
}
|
|
990
1205
|
else if (context.type === "VariableDeclarator") {
|
|
991
1206
|
const { id, init: _init, kind: _kind, ...rest } = context;
|
|
992
|
-
|
|
1207
|
+
block.body[block.body.length - 1] = {
|
|
993
1208
|
...rest,
|
|
994
1209
|
type: "ExpressionStatement",
|
|
995
1210
|
expression: {
|
|
@@ -1003,25 +1218,26 @@ function inlineWithArgs(state, func, call, context) {
|
|
|
1003
1218
|
}
|
|
1004
1219
|
else {
|
|
1005
1220
|
const side_exprs = inliner_unused(last.argument);
|
|
1006
|
-
|
|
1221
|
+
block.body.splice(block.body.length - 1, 1, ...side_exprs);
|
|
1007
1222
|
}
|
|
1008
1223
|
}
|
|
1009
1224
|
else {
|
|
1010
|
-
--
|
|
1225
|
+
--block.body.length;
|
|
1011
1226
|
}
|
|
1012
1227
|
}
|
|
1228
|
+
withLocDeep(body, context, context, true);
|
|
1013
1229
|
return body;
|
|
1014
1230
|
}
|
|
1015
1231
|
function inliner_inlineFunction(state, func, call, context) {
|
|
1016
1232
|
if (context) {
|
|
1017
1233
|
return inlineWithArgs(state, func, call, context);
|
|
1018
1234
|
}
|
|
1019
|
-
const retArg =
|
|
1235
|
+
const retArg = cloneDeep(func.node.body.body[0].argument);
|
|
1020
1236
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
1021
1237
|
const map = fixupLocalsMap(state);
|
|
1022
1238
|
const ret = processInlineBody(state, func, call, retArg, params);
|
|
1023
1239
|
state.localsStack[state.localsStack.length - 1].map = map;
|
|
1024
|
-
return ret;
|
|
1240
|
+
return ret && withLocDeep(ret, call, call, true);
|
|
1025
1241
|
}
|
|
1026
1242
|
function applyTypeIfNeeded(node) {
|
|
1027
1243
|
if ("enumType" in node && node.enumType) {
|
|
@@ -1126,6 +1342,121 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
1126
1342
|
return null;
|
|
1127
1343
|
}
|
|
1128
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
|
+
|
|
1129
1460
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
1130
1461
|
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
1131
1462
|
;// CONCATENATED MODULE: ./src/control-flow.ts
|
|
@@ -1271,7 +1602,8 @@ function control_flow_buildReducedGraph(state, func, notice) {
|
|
|
1271
1602
|
if (node.type === "WhileStatement") {
|
|
1272
1603
|
head = localState.newBlock(top.continue);
|
|
1273
1604
|
state.traverse(node.test);
|
|
1274
|
-
localState.addEdge(localState.
|
|
1605
|
+
localState.addEdge(localState.curBlock, top.break);
|
|
1606
|
+
localState.newBlock();
|
|
1275
1607
|
}
|
|
1276
1608
|
else {
|
|
1277
1609
|
head = localState.newBlock();
|
|
@@ -1592,6 +1924,8 @@ function cleanCfg(head) {
|
|
|
1592
1924
|
}
|
|
1593
1925
|
}));
|
|
1594
1926
|
}
|
|
1927
|
+
if (!cur.node)
|
|
1928
|
+
cur.node = succ.node;
|
|
1595
1929
|
}
|
|
1596
1930
|
}
|
|
1597
1931
|
});
|
|
@@ -1645,6 +1979,8 @@ var priorityqueuejs = __webpack_require__(2789);
|
|
|
1645
1979
|
|
|
1646
1980
|
|
|
1647
1981
|
|
|
1982
|
+
|
|
1983
|
+
|
|
1648
1984
|
/**
|
|
1649
1985
|
* This implements a pseudo Partial Redundancy Elimination
|
|
1650
1986
|
* pass. It isn't quite like traditional PRE because we're
|
|
@@ -1690,6 +2026,20 @@ function declName(decl) {
|
|
|
1690
2026
|
throw new Error(`Unexpected EventDecl type: ${decl.type}`);
|
|
1691
2027
|
}
|
|
1692
2028
|
}
|
|
2029
|
+
function logAntState(s, decl) {
|
|
2030
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
2031
|
+
if (event.type === "def" || event.type === "mod")
|
|
2032
|
+
defs++;
|
|
2033
|
+
return defs;
|
|
2034
|
+
}, 0);
|
|
2035
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
2036
|
+
console.log(` - members: ${Array.from(s.members)
|
|
2037
|
+
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
2038
|
+
.join(", ")}`);
|
|
2039
|
+
}
|
|
2040
|
+
function logAntDecls(antDecls) {
|
|
2041
|
+
antDecls.forEach(logAntState);
|
|
2042
|
+
}
|
|
1693
2043
|
function pre_sizeBasedPRE(state, func) {
|
|
1694
2044
|
if (!func.node.body)
|
|
1695
2045
|
return;
|
|
@@ -1700,18 +2050,11 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1700
2050
|
return;
|
|
1701
2051
|
}
|
|
1702
2052
|
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
1703
|
-
const candidates = computeAttributes(head);
|
|
2053
|
+
const candidates = computeAttributes(state, head);
|
|
1704
2054
|
if (candidates) {
|
|
1705
2055
|
if (logging) {
|
|
1706
2056
|
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
1707
|
-
candidates
|
|
1708
|
-
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
1709
|
-
if (event.type === "def")
|
|
1710
|
-
defs++;
|
|
1711
|
-
return defs;
|
|
1712
|
-
}, 0);
|
|
1713
|
-
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
|
|
1714
|
-
});
|
|
2057
|
+
logAntDecls(candidates);
|
|
1715
2058
|
}
|
|
1716
2059
|
const nodeMap = new Map();
|
|
1717
2060
|
const declMap = new Map();
|
|
@@ -1727,8 +2070,10 @@ function pre_sizeBasedPRE(state, func) {
|
|
|
1727
2070
|
let i = 0;
|
|
1728
2071
|
do {
|
|
1729
2072
|
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
1730
|
-
if (!identifiers.has(name))
|
|
2073
|
+
if (!identifiers.has(name)) {
|
|
2074
|
+
identifiers.add(name);
|
|
1731
2075
|
break;
|
|
2076
|
+
}
|
|
1732
2077
|
i++;
|
|
1733
2078
|
} while (true);
|
|
1734
2079
|
declMap.set(decl, name);
|
|
@@ -1819,11 +2164,14 @@ function buildPREGraph(state, func) {
|
|
|
1819
2164
|
case "ParenthesizedExpression":
|
|
1820
2165
|
break;
|
|
1821
2166
|
case "Literal":
|
|
1822
|
-
if (
|
|
1823
|
-
|
|
2167
|
+
if (refCost(node) > LocalRefCost) {
|
|
2168
|
+
const key = LiteralIntegerRe.test(node.raw)
|
|
2169
|
+
? BigInt(node.value)
|
|
2170
|
+
: node.value;
|
|
2171
|
+
let decl = literals.get(key);
|
|
1824
2172
|
if (!decl) {
|
|
1825
2173
|
decl = node;
|
|
1826
|
-
literals.set(
|
|
2174
|
+
literals.set(key, decl);
|
|
1827
2175
|
}
|
|
1828
2176
|
return {
|
|
1829
2177
|
type: "ref",
|
|
@@ -1903,10 +2251,18 @@ function buildPREGraph(state, func) {
|
|
|
1903
2251
|
}
|
|
1904
2252
|
break;
|
|
1905
2253
|
}
|
|
1906
|
-
case "NewExpression":
|
|
1907
|
-
|
|
2254
|
+
case "NewExpression": {
|
|
2255
|
+
const [, results] = state.lookup(node.callee);
|
|
2256
|
+
const callees = results ? findCalleesForNew(results) : null;
|
|
2257
|
+
liveDef(null, stmt);
|
|
2258
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2259
|
+
}
|
|
2260
|
+
case "CallExpression": {
|
|
1908
2261
|
liveDef(null, stmt);
|
|
1909
|
-
|
|
2262
|
+
const [, results] = state.lookup(node.callee);
|
|
2263
|
+
const callees = results ? findCallees(results) : null;
|
|
2264
|
+
return { type: "mod", node, mayThrow, callees };
|
|
2265
|
+
}
|
|
1910
2266
|
default:
|
|
1911
2267
|
if (!isExpression(node))
|
|
1912
2268
|
break;
|
|
@@ -1922,12 +2278,6 @@ function buildPREGraph(state, func) {
|
|
|
1922
2278
|
function anticipatedDecls() {
|
|
1923
2279
|
return new Map();
|
|
1924
2280
|
}
|
|
1925
|
-
function cloneSet(ae) {
|
|
1926
|
-
return new Set(ae);
|
|
1927
|
-
}
|
|
1928
|
-
function mergeSet(a, b) {
|
|
1929
|
-
b.forEach((event) => a.add(event));
|
|
1930
|
-
}
|
|
1931
2281
|
function equalSet(a, b) {
|
|
1932
2282
|
if (a.size != b.size)
|
|
1933
2283
|
return false;
|
|
@@ -1957,21 +2307,28 @@ function cloneAnticipatedState(as) {
|
|
|
1957
2307
|
members: new Map(as.members),
|
|
1958
2308
|
};
|
|
1959
2309
|
}
|
|
2310
|
+
function mergeAnticipatedState(ae, be) {
|
|
2311
|
+
mergeSet(ae.ant, be.ant);
|
|
2312
|
+
be.members.forEach((live, block) => ae.members.set(block, live));
|
|
2313
|
+
if (be.live)
|
|
2314
|
+
ae.live = true;
|
|
2315
|
+
}
|
|
1960
2316
|
function cloneAnticipatedDecls(ad) {
|
|
1961
2317
|
const copy = anticipatedDecls();
|
|
1962
2318
|
for (const [k, v] of ad) {
|
|
1963
|
-
|
|
2319
|
+
if (!v.isIsolated) {
|
|
2320
|
+
copy.set(k, cloneAnticipatedState(v));
|
|
2321
|
+
}
|
|
1964
2322
|
}
|
|
1965
2323
|
return copy;
|
|
1966
2324
|
}
|
|
1967
2325
|
function mergeAnticipatedDecls(a, b) {
|
|
1968
2326
|
for (const [k, v] of b) {
|
|
2327
|
+
if (v.isIsolated)
|
|
2328
|
+
continue;
|
|
1969
2329
|
const ae = a.get(k);
|
|
1970
2330
|
if (ae) {
|
|
1971
|
-
|
|
1972
|
-
v.members.forEach((live, block) => ae.members.set(block, live));
|
|
1973
|
-
if (v.live)
|
|
1974
|
-
ae.live = true;
|
|
2331
|
+
mergeAnticipatedState(ae, v);
|
|
1975
2332
|
}
|
|
1976
2333
|
else {
|
|
1977
2334
|
a.set(k, cloneAnticipatedState(v));
|
|
@@ -1985,6 +2342,7 @@ function equalStates(a, b) {
|
|
|
1985
2342
|
const be = b.get(k);
|
|
1986
2343
|
if (!be ||
|
|
1987
2344
|
be.live != ae.live ||
|
|
2345
|
+
be.isIsolated != ae.isIsolated ||
|
|
1988
2346
|
!equalSet(ae.ant, be.ant) ||
|
|
1989
2347
|
!equalMap(ae.members, be.members)) {
|
|
1990
2348
|
return false;
|
|
@@ -1998,6 +2356,7 @@ function refCost(node) {
|
|
|
1998
2356
|
switch (typeof node.value) {
|
|
1999
2357
|
case "string":
|
|
2000
2358
|
return 5;
|
|
2359
|
+
case "bigint":
|
|
2001
2360
|
case "number":
|
|
2002
2361
|
return 5;
|
|
2003
2362
|
case "boolean":
|
|
@@ -2055,10 +2414,11 @@ function candidateCost(candState) {
|
|
|
2055
2414
|
cost += defCost(candState.node);
|
|
2056
2415
|
}
|
|
2057
2416
|
});
|
|
2058
|
-
|
|
2417
|
+
const boundarySize = candidateBoundary(candState).size;
|
|
2418
|
+
cost += defCost(candState.node) * boundarySize;
|
|
2059
2419
|
return cost;
|
|
2060
2420
|
}
|
|
2061
|
-
function computeAttributes(head) {
|
|
2421
|
+
function computeAttributes(state, head) {
|
|
2062
2422
|
const order = getPostOrder(head);
|
|
2063
2423
|
order.forEach((block, i) => {
|
|
2064
2424
|
block.order = i;
|
|
@@ -2167,7 +2527,9 @@ function computeAttributes(head) {
|
|
|
2167
2527
|
curState.forEach((candidates, decl) => {
|
|
2168
2528
|
if (decl.type === "VariableDeclarator" &&
|
|
2169
2529
|
decl.node.kind === "var" &&
|
|
2170
|
-
candidates.live
|
|
2530
|
+
candidates.live &&
|
|
2531
|
+
(!event.callees ||
|
|
2532
|
+
event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
|
|
2171
2533
|
candidates.ant.add(getMod(event, decl, candidates.node));
|
|
2172
2534
|
candidates.live = false;
|
|
2173
2535
|
}
|
|
@@ -2193,9 +2555,7 @@ function computeAttributes(head) {
|
|
|
2193
2555
|
if (isUpdate || candidates.live) {
|
|
2194
2556
|
candidates.ant.add(event);
|
|
2195
2557
|
}
|
|
2196
|
-
|
|
2197
|
-
candidates.live = false;
|
|
2198
|
-
}
|
|
2558
|
+
candidates.live = isUpdate;
|
|
2199
2559
|
break;
|
|
2200
2560
|
}
|
|
2201
2561
|
}
|
|
@@ -2204,12 +2564,23 @@ function computeAttributes(head) {
|
|
|
2204
2564
|
curState.forEach((antState) => {
|
|
2205
2565
|
antState.head = top;
|
|
2206
2566
|
antState.members.set(top, antState.live);
|
|
2567
|
+
if (!antState.live && candidateBoundary(antState).size === 0) {
|
|
2568
|
+
// we found a group that's isolated from the rest
|
|
2569
|
+
// of the function. Don't merge it with earlier
|
|
2570
|
+
// refs and defs, because we can take it or leave
|
|
2571
|
+
// it based on its own cost.
|
|
2572
|
+
antState.isIsolated = true;
|
|
2573
|
+
}
|
|
2207
2574
|
});
|
|
2208
2575
|
const oldState = blockStates[top.order];
|
|
2209
2576
|
if (oldState && equalStates(oldState, curState)) {
|
|
2210
2577
|
continue;
|
|
2211
2578
|
}
|
|
2212
2579
|
blockStates[top.order] = curState;
|
|
2580
|
+
if (logging) {
|
|
2581
|
+
console.log(`Updated block ${top.order}`);
|
|
2582
|
+
logAntDecls(curState);
|
|
2583
|
+
}
|
|
2213
2584
|
if (top.preds) {
|
|
2214
2585
|
top.preds.forEach((pred) => enqueue(pred));
|
|
2215
2586
|
}
|
|
@@ -2222,7 +2593,9 @@ function computeAttributes(head) {
|
|
|
2222
2593
|
if (cost >= 0)
|
|
2223
2594
|
return;
|
|
2224
2595
|
const existing = candidateDecls.get(decl);
|
|
2225
|
-
if (!existing ||
|
|
2596
|
+
if (!existing ||
|
|
2597
|
+
existing.isIsolated ||
|
|
2598
|
+
candidateCost(existing) > cost) {
|
|
2226
2599
|
const boundary = candidateBoundary(events);
|
|
2227
2600
|
if (!Array.from(boundary).every((block) => {
|
|
2228
2601
|
if (block !== events.head && block.events) {
|
|
@@ -2262,7 +2635,11 @@ function computeAttributes(head) {
|
|
|
2262
2635
|
return;
|
|
2263
2636
|
}
|
|
2264
2637
|
events.live = false;
|
|
2265
|
-
if (
|
|
2638
|
+
if (existing && existing.isIsolated) {
|
|
2639
|
+
delete existing.isIsolated;
|
|
2640
|
+
mergeAnticipatedState(events, existing);
|
|
2641
|
+
}
|
|
2642
|
+
else if (candidateCost(events) != cost) {
|
|
2266
2643
|
throw new Error(`cost of block ${i} changed`);
|
|
2267
2644
|
}
|
|
2268
2645
|
candidateDecls.set(decl, events);
|
|
@@ -2341,20 +2718,26 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2341
2718
|
stmtStack.pop();
|
|
2342
2719
|
const events = nodeMap.get(node);
|
|
2343
2720
|
if (events) {
|
|
2344
|
-
|
|
2345
|
-
if (
|
|
2721
|
+
const ret = events.reduce((ret, event) => {
|
|
2722
|
+
if (event.type === "ref") {
|
|
2723
|
+
if (ret) {
|
|
2724
|
+
throw new Error(`ref found when there was already a replacement for this node`);
|
|
2725
|
+
}
|
|
2346
2726
|
if (node.type !== "Identifier" &&
|
|
2347
2727
|
node.type !== "MemberExpression" &&
|
|
2348
2728
|
node.type !== "Literal") {
|
|
2349
2729
|
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
2350
2730
|
}
|
|
2351
|
-
const name = declMap.get(
|
|
2731
|
+
const name = declMap.get(event.decl);
|
|
2352
2732
|
if (!name) {
|
|
2353
2733
|
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
2354
2734
|
}
|
|
2355
2735
|
return ident(name, node);
|
|
2356
2736
|
}
|
|
2357
|
-
|
|
2737
|
+
if (event.type === "def") {
|
|
2738
|
+
if (ret) {
|
|
2739
|
+
throw new Error(`def found when there was already a replacement for this node`);
|
|
2740
|
+
}
|
|
2358
2741
|
if (node.type !== "AssignmentExpression" &&
|
|
2359
2742
|
node.type !== "UpdateExpression") {
|
|
2360
2743
|
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
@@ -2362,7 +2745,7 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2362
2745
|
const target = node.type === "AssignmentExpression"
|
|
2363
2746
|
? node.left
|
|
2364
2747
|
: node.argument;
|
|
2365
|
-
const name = declMap.get(
|
|
2748
|
+
const name = declMap.get(event.decl);
|
|
2366
2749
|
if (!name) {
|
|
2367
2750
|
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
2368
2751
|
}
|
|
@@ -2381,20 +2764,24 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2381
2764
|
}
|
|
2382
2765
|
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
2383
2766
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2767
|
+
if (event.type === "mod") {
|
|
2768
|
+
if (!event.decl) {
|
|
2769
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
2770
|
+
}
|
|
2771
|
+
let pending = pendingMap.get(stmt);
|
|
2772
|
+
if (!pending) {
|
|
2773
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
2774
|
+
}
|
|
2775
|
+
pending.add(event);
|
|
2391
2776
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
pendingMap.set(stmt, (pending = new Set()));
|
|
2777
|
+
else {
|
|
2778
|
+
throw new Error(`Unexpected ${event.type} found`);
|
|
2395
2779
|
}
|
|
2396
|
-
|
|
2397
|
-
});
|
|
2780
|
+
return ret;
|
|
2781
|
+
}, null);
|
|
2782
|
+
if (ret) {
|
|
2783
|
+
return ret;
|
|
2784
|
+
}
|
|
2398
2785
|
}
|
|
2399
2786
|
const pending = pendingMap.get(node);
|
|
2400
2787
|
if (node.type === "SequenceExpression") {
|
|
@@ -2468,6 +2855,148 @@ function applyReplacements(func, nodeMap, declMap) {
|
|
|
2468
2855
|
});
|
|
2469
2856
|
}
|
|
2470
2857
|
|
|
2858
|
+
;// CONCATENATED MODULE: ./src/unused-exprs.ts
|
|
2859
|
+
|
|
2860
|
+
|
|
2861
|
+
|
|
2862
|
+
function unused_exprs_cleanupUnusedVars(state, node) {
|
|
2863
|
+
const [parent] = state.stack.slice(-1);
|
|
2864
|
+
if (parent.node !== node) {
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (parent.type != "BlockStatement") {
|
|
2868
|
+
throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
|
|
2869
|
+
}
|
|
2870
|
+
if (parent.decls) {
|
|
2871
|
+
let toRemove = null;
|
|
2872
|
+
Object.values(parent.decls).forEach((decls) => {
|
|
2873
|
+
if (decls.length === 1 &&
|
|
2874
|
+
decls[0].type === "VariableDeclarator" &&
|
|
2875
|
+
!decls[0].used) {
|
|
2876
|
+
if (!toRemove)
|
|
2877
|
+
toRemove = {};
|
|
2878
|
+
toRemove[decls[0].name] = decls[0];
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2881
|
+
if (toRemove) {
|
|
2882
|
+
const varDeclarations = new Map();
|
|
2883
|
+
traverseAst(node, null, (node) => {
|
|
2884
|
+
switch (node.type) {
|
|
2885
|
+
case "VariableDeclaration": {
|
|
2886
|
+
node.declarations.forEach((decl, i) => {
|
|
2887
|
+
const name = variableDeclarationName(decl.id);
|
|
2888
|
+
if (hasProperty(toRemove, name)) {
|
|
2889
|
+
const indices = varDeclarations.get(node);
|
|
2890
|
+
if (indices) {
|
|
2891
|
+
indices.push(i);
|
|
2892
|
+
}
|
|
2893
|
+
else {
|
|
2894
|
+
varDeclarations.set(node, [i]);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2898
|
+
break;
|
|
2899
|
+
}
|
|
2900
|
+
case "ExpressionStatement":
|
|
2901
|
+
if (node.expression.type === "AssignmentExpression") {
|
|
2902
|
+
if (node.expression.left.type === "Identifier" &&
|
|
2903
|
+
hasProperty(toRemove, node.expression.left.name)) {
|
|
2904
|
+
return unused(node.expression.right);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
else if (node.expression.type === "UpdateExpression" &&
|
|
2908
|
+
node.expression.argument.type === "Identifier" &&
|
|
2909
|
+
hasProperty(toRemove, node.expression.argument.name)) {
|
|
2910
|
+
return false;
|
|
2911
|
+
}
|
|
2912
|
+
break;
|
|
2913
|
+
case "SequenceExpression": {
|
|
2914
|
+
for (let i = node.expressions.length; i--;) {
|
|
2915
|
+
const expr = node.expressions[i];
|
|
2916
|
+
if (expr.type === "AssignmentExpression") {
|
|
2917
|
+
if (expr.left.type === "Identifier" &&
|
|
2918
|
+
hasProperty(toRemove, expr.left.name)) {
|
|
2919
|
+
const rep = unused(expr.right);
|
|
2920
|
+
if (!rep.length) {
|
|
2921
|
+
node.expressions.splice(i, 1);
|
|
2922
|
+
}
|
|
2923
|
+
else {
|
|
2924
|
+
// Sequence expressions can only be assignments
|
|
2925
|
+
// or update expressions. Even calls aren't allowed
|
|
2926
|
+
toRemove[expr.left.name] = null;
|
|
2927
|
+
expr.operator = "=";
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
else if (expr.type === "UpdateExpression" &&
|
|
2932
|
+
expr.argument.type === "Identifier" &&
|
|
2933
|
+
hasProperty(toRemove, expr.argument.name)) {
|
|
2934
|
+
node.expressions.splice(i, 1);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
break;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
return null;
|
|
2941
|
+
});
|
|
2942
|
+
varDeclarations.forEach((indices, decl) => {
|
|
2943
|
+
let index = -1;
|
|
2944
|
+
for (let ii = indices.length, j = decl.declarations.length; ii--;) {
|
|
2945
|
+
const i = indices[ii];
|
|
2946
|
+
const vdecl = decl.declarations[i];
|
|
2947
|
+
const name = variableDeclarationName(vdecl.id);
|
|
2948
|
+
if (hasProperty(toRemove, name)) {
|
|
2949
|
+
const rep = vdecl.init ? unused(vdecl.init) : [];
|
|
2950
|
+
if (rep.length) {
|
|
2951
|
+
if (parent.node.type === "ForStatement") {
|
|
2952
|
+
// declarations whose inits have side effects
|
|
2953
|
+
// can't be deleted from for statements.
|
|
2954
|
+
continue;
|
|
2955
|
+
}
|
|
2956
|
+
if (index < 0) {
|
|
2957
|
+
index = parent.node.body.findIndex((s) => s === decl);
|
|
2958
|
+
if (index < 0) {
|
|
2959
|
+
throw new Error(`Failed to find variable declaration for ${variableDeclarationName(vdecl.id)}`);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
if (j > i + 1) {
|
|
2963
|
+
const tail = {
|
|
2964
|
+
...decl,
|
|
2965
|
+
declarations: decl.declarations.slice(i + 1, j),
|
|
2966
|
+
};
|
|
2967
|
+
if (decl.loc && vdecl.loc) {
|
|
2968
|
+
tail.loc = { ...decl.loc, start: vdecl.loc.end };
|
|
2969
|
+
tail.start = vdecl.end;
|
|
2970
|
+
}
|
|
2971
|
+
rep.push(tail);
|
|
2972
|
+
}
|
|
2973
|
+
if (decl.loc && vdecl.loc) {
|
|
2974
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
2975
|
+
decl.end = vdecl.start;
|
|
2976
|
+
}
|
|
2977
|
+
decl.declarations.splice(i);
|
|
2978
|
+
parent.node.body.splice(index + 1, 0, ...rep);
|
|
2979
|
+
j = i;
|
|
2980
|
+
continue;
|
|
2981
|
+
}
|
|
2982
|
+
if (toRemove[name]) {
|
|
2983
|
+
j--;
|
|
2984
|
+
decl.declarations.splice(i, 1);
|
|
2985
|
+
if (i === j && decl.loc && vdecl.loc) {
|
|
2986
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
2987
|
+
decl.end = vdecl.start;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
else {
|
|
2991
|
+
delete vdecl.init;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
|
|
2471
3000
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
2472
3001
|
|
|
2473
3002
|
function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
@@ -2532,14 +3061,16 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2532
3061
|
return checkResults(state.lookup(node), node);
|
|
2533
3062
|
}
|
|
2534
3063
|
break;
|
|
2535
|
-
case "MemberExpression":
|
|
2536
|
-
|
|
2537
|
-
|
|
3064
|
+
case "MemberExpression": {
|
|
3065
|
+
const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
|
|
3066
|
+
if (property) {
|
|
3067
|
+
if (!name || property.name === name) {
|
|
2538
3068
|
return checkResults(state.lookup(node), node) || ["object"];
|
|
2539
3069
|
}
|
|
2540
3070
|
return ["object"];
|
|
2541
3071
|
}
|
|
2542
3072
|
break;
|
|
3073
|
+
}
|
|
2543
3074
|
case "MethodDefinition": {
|
|
2544
3075
|
if (!state.inType) {
|
|
2545
3076
|
throw new Error("Method definition outside of type!");
|
|
@@ -2548,7 +3079,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2548
3079
|
node.params.forEach((param) => {
|
|
2549
3080
|
if (param.type == "BinaryExpression") {
|
|
2550
3081
|
state.traverse(param.right);
|
|
2551
|
-
state.inType = true;
|
|
2552
3082
|
}
|
|
2553
3083
|
});
|
|
2554
3084
|
}
|
|
@@ -2571,6 +3101,9 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
|
|
|
2571
3101
|
|
|
2572
3102
|
|
|
2573
3103
|
|
|
3104
|
+
|
|
3105
|
+
|
|
3106
|
+
|
|
2574
3107
|
function collectClassInfo(state) {
|
|
2575
3108
|
const toybox = state.stack[0].decls["Toybox"][0];
|
|
2576
3109
|
const lang = toybox.decls["Lang"][0];
|
|
@@ -2630,8 +3163,7 @@ function collectClassInfo(state) {
|
|
|
2630
3163
|
c.decls &&
|
|
2631
3164
|
Object.values(c.decls).forEach((funcs) => {
|
|
2632
3165
|
funcs.forEach((f) => {
|
|
2633
|
-
if (
|
|
2634
|
-
f.type === "FunctionDeclaration" &&
|
|
3166
|
+
if (f.type === "FunctionDeclaration" &&
|
|
2635
3167
|
hasProperty(cls.decls, f.name)) {
|
|
2636
3168
|
f.node.hasOverride = true;
|
|
2637
3169
|
}
|
|
@@ -2644,6 +3176,15 @@ function collectClassInfo(state) {
|
|
|
2644
3176
|
state.allClasses.forEach((elm) => {
|
|
2645
3177
|
if (elm.superClass)
|
|
2646
3178
|
markOverrides(elm, elm.superClass);
|
|
3179
|
+
if (elm.hasInvoke && elm.decls) {
|
|
3180
|
+
Object.values(elm.decls).forEach((funcs) => {
|
|
3181
|
+
funcs.forEach((f) => {
|
|
3182
|
+
if (f.type === "FunctionDeclaration" && !f.isStatic) {
|
|
3183
|
+
markInvokeClassMethod(f);
|
|
3184
|
+
}
|
|
3185
|
+
});
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
2647
3188
|
});
|
|
2648
3189
|
}
|
|
2649
3190
|
function getFileSources(fnMap) {
|
|
@@ -2683,7 +3224,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2683
3224
|
const preState = {
|
|
2684
3225
|
fnMap,
|
|
2685
3226
|
config,
|
|
2686
|
-
allFunctions:
|
|
3227
|
+
allFunctions: {},
|
|
2687
3228
|
allClasses: [],
|
|
2688
3229
|
shouldExclude(node) {
|
|
2689
3230
|
if ("attrs" in node &&
|
|
@@ -2713,11 +3254,6 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2713
3254
|
pre(node, state) {
|
|
2714
3255
|
switch (node.type) {
|
|
2715
3256
|
case "FunctionDeclaration":
|
|
2716
|
-
if (markApi) {
|
|
2717
|
-
node.body = null;
|
|
2718
|
-
break;
|
|
2719
|
-
}
|
|
2720
|
-
// falls through
|
|
2721
3257
|
case "ModuleDeclaration":
|
|
2722
3258
|
case "ClassDeclaration": {
|
|
2723
3259
|
const [scope] = state.stack.slice(-1);
|
|
@@ -2728,7 +3264,18 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2728
3264
|
(scope.node.attrs &&
|
|
2729
3265
|
scope.node.attrs.access &&
|
|
2730
3266
|
scope.node.attrs.access.includes("static"));
|
|
2731
|
-
|
|
3267
|
+
if (markApi) {
|
|
3268
|
+
node.body = null;
|
|
3269
|
+
scope.info = getApiFunctionInfo(scope);
|
|
3270
|
+
delete scope.stack;
|
|
3271
|
+
}
|
|
3272
|
+
const allFuncs = state.allFunctions;
|
|
3273
|
+
if (!hasProperty(allFuncs, scope.name)) {
|
|
3274
|
+
allFuncs[scope.name] = [scope];
|
|
3275
|
+
}
|
|
3276
|
+
else {
|
|
3277
|
+
allFuncs[scope.name].push(scope);
|
|
3278
|
+
}
|
|
2732
3279
|
}
|
|
2733
3280
|
else if (scope.type === "ClassDeclaration") {
|
|
2734
3281
|
state.allClasses.push(scope);
|
|
@@ -2753,7 +3300,7 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2753
3300
|
value.hasTests = hasTests;
|
|
2754
3301
|
});
|
|
2755
3302
|
delete state.shouldExclude;
|
|
2756
|
-
delete state.
|
|
3303
|
+
delete state.pre;
|
|
2757
3304
|
collectClassInfo(state);
|
|
2758
3305
|
const diagnosticType = config?.checkInvalidSymbols !== "OFF"
|
|
2759
3306
|
? config?.checkInvalidSymbols || "WARNING"
|
|
@@ -2776,6 +3323,8 @@ async function analyze(fnMap, barrelList, config) {
|
|
|
2776
3323
|
});
|
|
2777
3324
|
});
|
|
2778
3325
|
}
|
|
3326
|
+
state.exposed = state.nextExposed;
|
|
3327
|
+
state.nextExposed = {};
|
|
2779
3328
|
return state;
|
|
2780
3329
|
}
|
|
2781
3330
|
function compareLiteralLike(a, b) {
|
|
@@ -2821,15 +3370,12 @@ function getLiteralNode(node) {
|
|
|
2821
3370
|
if (node.argument.type != "Literal")
|
|
2822
3371
|
return null;
|
|
2823
3372
|
switch (node.operator) {
|
|
2824
|
-
case "-":
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
value: -node.argument.value,
|
|
2829
|
-
raw: "-" + node.argument.value,
|
|
2830
|
-
enumType: node.enumType,
|
|
2831
|
-
};
|
|
3373
|
+
case "-": {
|
|
3374
|
+
const [arg, type] = getNodeValue(node.argument);
|
|
3375
|
+
if (type === "Number" || type === "Long") {
|
|
3376
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2832
3377
|
}
|
|
3378
|
+
}
|
|
2833
3379
|
}
|
|
2834
3380
|
}
|
|
2835
3381
|
return null;
|
|
@@ -2848,29 +3394,29 @@ function getNodeValue(node) {
|
|
|
2848
3394
|
if (node.type != "Literal") {
|
|
2849
3395
|
return [null, null];
|
|
2850
3396
|
}
|
|
2851
|
-
|
|
3397
|
+
if (node.value === null) {
|
|
3398
|
+
return [node, "Null"];
|
|
3399
|
+
}
|
|
3400
|
+
const type = typeof node.value;
|
|
2852
3401
|
if (type === "number") {
|
|
2853
|
-
const match =
|
|
3402
|
+
const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
|
|
2854
3403
|
if (match) {
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
type = "Double";
|
|
2859
|
-
}
|
|
2860
|
-
else {
|
|
2861
|
-
type = "Float";
|
|
3404
|
+
return match[2] === "l" || match[2] === "L"
|
|
3405
|
+
? [node, "Long"]
|
|
3406
|
+
: [node, "Number"];
|
|
2862
3407
|
}
|
|
3408
|
+
return [node, node.raw.endsWith("d") ? "Double" : "Float"];
|
|
2863
3409
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
3410
|
+
if (type === "bigint") {
|
|
3411
|
+
return [node, "Long"];
|
|
2866
3412
|
}
|
|
2867
|
-
|
|
2868
|
-
|
|
3413
|
+
if (type === "string") {
|
|
3414
|
+
return [node, "String"];
|
|
2869
3415
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
3416
|
+
if (type === "boolean") {
|
|
3417
|
+
return [node, "Boolean"];
|
|
2872
3418
|
}
|
|
2873
|
-
|
|
3419
|
+
throw new Error(`Literal has unknown type '${type}'`);
|
|
2874
3420
|
}
|
|
2875
3421
|
function fullTypeName(state, tsp) {
|
|
2876
3422
|
if (typeof tsp.name === "string") {
|
|
@@ -2915,6 +3461,46 @@ function isBooleanExpression(state, node) {
|
|
|
2915
3461
|
}
|
|
2916
3462
|
return false;
|
|
2917
3463
|
}
|
|
3464
|
+
function replacementLiteral(arg, value, type) {
|
|
3465
|
+
if (typeof value === "boolean") {
|
|
3466
|
+
type = "Boolean";
|
|
3467
|
+
}
|
|
3468
|
+
else if (type === "Number") {
|
|
3469
|
+
value = Number(BigInt.asIntN(32, BigInt(value)));
|
|
3470
|
+
}
|
|
3471
|
+
else if (type === "Long") {
|
|
3472
|
+
value = BigInt.asIntN(64, BigInt(value));
|
|
3473
|
+
}
|
|
3474
|
+
return {
|
|
3475
|
+
...arg,
|
|
3476
|
+
value,
|
|
3477
|
+
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
const operators = {
|
|
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,
|
|
3486
|
+
"&": (left, right) => left & right,
|
|
3487
|
+
"|": (left, right) => left | right,
|
|
3488
|
+
"^": (left, right) => left ^ right,
|
|
3489
|
+
"<<": (left, right) => left << (right & 127n),
|
|
3490
|
+
">>": (left, right) => left >> (right & 127n),
|
|
3491
|
+
"==": (left, right) =>
|
|
3492
|
+
// two string literals will compare unequal, becuase string
|
|
3493
|
+
// equality is object equality.
|
|
3494
|
+
typeof left === "string" ? false : left === right,
|
|
3495
|
+
"!=": (left, right) => typeof left === "string" ? true : left !== right,
|
|
3496
|
+
"<=": (left, right) => left <= right,
|
|
3497
|
+
">=": (left, right) => left >= right,
|
|
3498
|
+
"<": (left, right) => left < right,
|
|
3499
|
+
">": (left, right) => left > right,
|
|
3500
|
+
as: null,
|
|
3501
|
+
instanceof: null,
|
|
3502
|
+
has: null,
|
|
3503
|
+
};
|
|
2918
3504
|
function optimizeNode(state, node) {
|
|
2919
3505
|
switch (node.type) {
|
|
2920
3506
|
case "UnaryExpression": {
|
|
@@ -2929,29 +3515,17 @@ function optimizeNode(state, node) {
|
|
|
2929
3515
|
break;
|
|
2930
3516
|
case "-":
|
|
2931
3517
|
if (type === "Number" || type === "Long") {
|
|
2932
|
-
return
|
|
2933
|
-
...arg,
|
|
2934
|
-
value: -arg.value,
|
|
2935
|
-
raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
|
|
2936
|
-
};
|
|
3518
|
+
return replacementLiteral(arg, -arg.value, type);
|
|
2937
3519
|
}
|
|
2938
3520
|
break;
|
|
2939
3521
|
case "!":
|
|
2940
3522
|
case "~":
|
|
2941
3523
|
{
|
|
2942
|
-
let value;
|
|
2943
3524
|
if (type === "Number" || type === "Long") {
|
|
2944
|
-
|
|
3525
|
+
return replacementLiteral(arg, ~BigInt(arg.value), type);
|
|
2945
3526
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
}
|
|
2949
|
-
if (value !== undefined) {
|
|
2950
|
-
return {
|
|
2951
|
-
...arg,
|
|
2952
|
-
value,
|
|
2953
|
-
raw: value.toString() + (type === "Long" ? "l" : ""),
|
|
2954
|
-
};
|
|
3527
|
+
if (type === "Boolean" && node.operator == "!") {
|
|
3528
|
+
return replacementLiteral(arg, !arg.value, type);
|
|
2955
3529
|
}
|
|
2956
3530
|
}
|
|
2957
3531
|
break;
|
|
@@ -2959,27 +3533,6 @@ function optimizeNode(state, node) {
|
|
|
2959
3533
|
break;
|
|
2960
3534
|
}
|
|
2961
3535
|
case "BinaryExpression": {
|
|
2962
|
-
const operators = {
|
|
2963
|
-
"+": (left, right) => left + right,
|
|
2964
|
-
"-": (left, right) => left - right,
|
|
2965
|
-
"*": (left, right) => left * right,
|
|
2966
|
-
"/": (left, right) => Math.trunc(left / right),
|
|
2967
|
-
"%": (left, right) => left % right,
|
|
2968
|
-
"&": (left, right, type) => type === "Number" ? left & right : null,
|
|
2969
|
-
"|": (left, right, type) => type === "Number" ? left | right : null,
|
|
2970
|
-
"^": (left, right, type) => type === "Number" ? left ^ right : null,
|
|
2971
|
-
"<<": (left, right, type) => type === "Number" ? left << right : null,
|
|
2972
|
-
">>": (left, right, type) => type === "Number" ? left >> right : null,
|
|
2973
|
-
"==": (left, right) => left == right,
|
|
2974
|
-
"!=": (left, right) => left != right,
|
|
2975
|
-
"<=": (left, right) => left <= right,
|
|
2976
|
-
">=": (left, right) => left >= right,
|
|
2977
|
-
"<": (left, right) => left < right,
|
|
2978
|
-
">": (left, right) => left > right,
|
|
2979
|
-
as: null,
|
|
2980
|
-
instanceof: null,
|
|
2981
|
-
has: null,
|
|
2982
|
-
};
|
|
2983
3536
|
const op = operators[node.operator];
|
|
2984
3537
|
if (op) {
|
|
2985
3538
|
const [left, left_type] = getNodeValue(node.left);
|
|
@@ -2987,23 +3540,22 @@ function optimizeNode(state, node) {
|
|
|
2987
3540
|
if (!left || !right)
|
|
2988
3541
|
break;
|
|
2989
3542
|
let value = null;
|
|
2990
|
-
|
|
2991
|
-
|
|
3543
|
+
let type;
|
|
3544
|
+
if ((left_type != "Number" && left_type != "Long") ||
|
|
3545
|
+
left_type != right_type) {
|
|
2992
3546
|
if (node.operator !== "==" && node.operator !== "!=") {
|
|
2993
3547
|
break;
|
|
2994
3548
|
}
|
|
2995
3549
|
value = operators[node.operator](left.value, right.value);
|
|
3550
|
+
type = "Boolean";
|
|
2996
3551
|
}
|
|
2997
3552
|
else {
|
|
2998
|
-
|
|
3553
|
+
type = left_type;
|
|
3554
|
+
value = op(BigInt(left.value), BigInt(right.value));
|
|
2999
3555
|
}
|
|
3000
3556
|
if (value === null)
|
|
3001
3557
|
break;
|
|
3002
|
-
return
|
|
3003
|
-
...left,
|
|
3004
|
-
value,
|
|
3005
|
-
raw: value.toString() + (left_type === "Long" ? "l" : ""),
|
|
3006
|
-
};
|
|
3558
|
+
return replacementLiteral(left, value, type);
|
|
3007
3559
|
}
|
|
3008
3560
|
break;
|
|
3009
3561
|
}
|
|
@@ -3013,7 +3565,8 @@ function optimizeNode(state, node) {
|
|
|
3013
3565
|
break;
|
|
3014
3566
|
const falsy = left.value === false ||
|
|
3015
3567
|
left.value === null ||
|
|
3016
|
-
(
|
|
3568
|
+
((left_type === "Number" || left_type === "Long") &&
|
|
3569
|
+
(left.value === 0 || left.value === 0n));
|
|
3017
3570
|
if (falsy === (node.operator === "&&")) {
|
|
3018
3571
|
return left;
|
|
3019
3572
|
}
|
|
@@ -3054,9 +3607,7 @@ function evaluateFunction(state, func, args) {
|
|
|
3054
3607
|
const paramValues = args &&
|
|
3055
3608
|
Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
|
|
3056
3609
|
let ret = null;
|
|
3057
|
-
const body = args
|
|
3058
|
-
? JSON.parse(JSON.stringify(func.body))
|
|
3059
|
-
: func.body;
|
|
3610
|
+
const body = args ? cloneDeep(func.body) : func.body;
|
|
3060
3611
|
try {
|
|
3061
3612
|
traverseAst(body, (node) => {
|
|
3062
3613
|
switch (node.type) {
|
|
@@ -3107,12 +3658,10 @@ function markFunctionCalled(state, func) {
|
|
|
3107
3658
|
pushUnique(state.calledFunctions[func.id.name], func);
|
|
3108
3659
|
}
|
|
3109
3660
|
async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
3110
|
-
const state =
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
calledFunctions: {},
|
|
3115
|
-
};
|
|
3661
|
+
const state = (await analyze(fnMap, barrelList, config));
|
|
3662
|
+
state.localsStack = [{}];
|
|
3663
|
+
state.calledFunctions = {};
|
|
3664
|
+
state.usedByName = {};
|
|
3116
3665
|
const replace = (node, old) => {
|
|
3117
3666
|
if (node === false || node === null)
|
|
3118
3667
|
return node;
|
|
@@ -3201,6 +3750,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3201
3750
|
f.type == "FunctionDeclaration" &&
|
|
3202
3751
|
maybeCalled(f.node))) ||
|
|
3203
3752
|
(sc.superClass && checkInherited(sc, name))));
|
|
3753
|
+
const renamer = (idnode) => {
|
|
3754
|
+
const ident = idnode.type === "Identifier" ? idnode : idnode.left;
|
|
3755
|
+
const locals = topLocals();
|
|
3756
|
+
const { map } = locals;
|
|
3757
|
+
if (map) {
|
|
3758
|
+
const declName = ident.name;
|
|
3759
|
+
const name = renameVariable(state, locals, declName);
|
|
3760
|
+
if (name) {
|
|
3761
|
+
const [, results] = state.lookupValue(ident);
|
|
3762
|
+
if (!results) {
|
|
3763
|
+
throw new Error(`Didn't find local ${declName} which needed renaming`);
|
|
3764
|
+
}
|
|
3765
|
+
if (results.length !== 1) {
|
|
3766
|
+
throw new Error(`Lookup of local ${declName} found more than one result`);
|
|
3767
|
+
}
|
|
3768
|
+
const parent = results[0].parent;
|
|
3769
|
+
if (!parent) {
|
|
3770
|
+
throw new Error(`No parent in lookup of local ${declName}`);
|
|
3771
|
+
}
|
|
3772
|
+
const decls = parent.decls;
|
|
3773
|
+
if (!decls || !hasProperty(decls, declName)) {
|
|
3774
|
+
throw new Error(`Missing decls in lookup of local ${declName}`);
|
|
3775
|
+
}
|
|
3776
|
+
if (hasProperty(decls, name)) {
|
|
3777
|
+
throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
|
|
3778
|
+
}
|
|
3779
|
+
if (decls[declName].length === 1) {
|
|
3780
|
+
decls[name] = decls[declName];
|
|
3781
|
+
delete decls[declName];
|
|
3782
|
+
}
|
|
3783
|
+
else {
|
|
3784
|
+
let i = decls[declName].length;
|
|
3785
|
+
while (i--) {
|
|
3786
|
+
const decl = decls[declName][i];
|
|
3787
|
+
if (decl === idnode ||
|
|
3788
|
+
(decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
|
|
3789
|
+
decls[declName].splice(i, 1);
|
|
3790
|
+
decls[name] = [decl];
|
|
3791
|
+
break;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
if (i < 0) {
|
|
3795
|
+
throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
ident.name = name;
|
|
3799
|
+
}
|
|
3800
|
+
else {
|
|
3801
|
+
map[declName] = true;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
};
|
|
3204
3805
|
state.pre = (node) => {
|
|
3205
3806
|
switch (node.type) {
|
|
3206
3807
|
case "ConditionalExpression":
|
|
@@ -3221,7 +3822,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3221
3822
|
result = !!value.value;
|
|
3222
3823
|
}
|
|
3223
3824
|
if (result !== null) {
|
|
3224
|
-
node.test = {
|
|
3825
|
+
node.test = {
|
|
3826
|
+
type: "Literal",
|
|
3827
|
+
value: result,
|
|
3828
|
+
raw: result.toString(),
|
|
3829
|
+
};
|
|
3225
3830
|
if (node.type === "IfStatement" ||
|
|
3226
3831
|
node.type === "ConditionalExpression") {
|
|
3227
3832
|
return [result ? "consequent" : "alternate"];
|
|
@@ -3240,7 +3845,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3240
3845
|
return null;
|
|
3241
3846
|
}
|
|
3242
3847
|
case "EnumDeclaration":
|
|
3243
|
-
return
|
|
3848
|
+
return [];
|
|
3244
3849
|
case "ForStatement": {
|
|
3245
3850
|
const map = topLocals().map;
|
|
3246
3851
|
if (map) {
|
|
@@ -3249,43 +3854,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3249
3854
|
break;
|
|
3250
3855
|
}
|
|
3251
3856
|
case "VariableDeclarator": {
|
|
3252
|
-
|
|
3253
|
-
const { map } = locals;
|
|
3254
|
-
if (map) {
|
|
3255
|
-
const declName = variableDeclarationName(node.id);
|
|
3256
|
-
const name = renameVariable(state, locals, declName);
|
|
3257
|
-
if (name) {
|
|
3258
|
-
if (node.id.type === "Identifier") {
|
|
3259
|
-
node.id.name = name;
|
|
3260
|
-
}
|
|
3261
|
-
else {
|
|
3262
|
-
node.id.left.name = name;
|
|
3263
|
-
}
|
|
3264
|
-
}
|
|
3265
|
-
else {
|
|
3266
|
-
map[declName] = true;
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3857
|
+
renamer(node.id);
|
|
3269
3858
|
return ["init"];
|
|
3270
3859
|
}
|
|
3271
3860
|
case "CatchClause":
|
|
3272
3861
|
if (node.param) {
|
|
3273
3862
|
state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
|
|
3274
|
-
|
|
3275
|
-
const map = locals.map;
|
|
3276
|
-
const declName = variableDeclarationName(node.param);
|
|
3277
|
-
const name = renameVariable(state, locals, declName);
|
|
3278
|
-
if (name) {
|
|
3279
|
-
if (node.param.type === "Identifier") {
|
|
3280
|
-
node.param.name = name;
|
|
3281
|
-
}
|
|
3282
|
-
else {
|
|
3283
|
-
node.param.left.name = name;
|
|
3284
|
-
}
|
|
3285
|
-
}
|
|
3286
|
-
else {
|
|
3287
|
-
map[declName] = true;
|
|
3288
|
-
}
|
|
3863
|
+
renamer(node.param);
|
|
3289
3864
|
return ["body"];
|
|
3290
3865
|
}
|
|
3291
3866
|
break;
|
|
@@ -3301,14 +3876,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3301
3876
|
break;
|
|
3302
3877
|
case "UnaryExpression":
|
|
3303
3878
|
if (node.operator == ":") {
|
|
3304
|
-
//
|
|
3305
|
-
//
|
|
3306
|
-
// indirectly, so we can't remove any enums or
|
|
3307
|
-
// constants with that name (we can still replace
|
|
3308
|
-
// uses of those constants though).
|
|
3309
|
-
state.exposed[node.argument.name] = true;
|
|
3310
|
-
// In any case, we can't replace *this* use of the
|
|
3311
|
-
// symbol with its value...
|
|
3879
|
+
// node.argument is not a normal identifier.
|
|
3880
|
+
// don't visit it.
|
|
3312
3881
|
return [];
|
|
3313
3882
|
}
|
|
3314
3883
|
break;
|
|
@@ -3320,29 +3889,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3320
3889
|
if (typeof name === "string") {
|
|
3321
3890
|
node.name = name;
|
|
3322
3891
|
}
|
|
3892
|
+
const [, results] = state.lookupValue(node);
|
|
3893
|
+
if (results) {
|
|
3894
|
+
if (results.length !== 1 || results[0].results.length !== 1) {
|
|
3895
|
+
throw new Error(`Local ${node.name} had multiple lookup results`);
|
|
3896
|
+
}
|
|
3897
|
+
const parent = results[0].parent;
|
|
3898
|
+
if (!parent) {
|
|
3899
|
+
throw new Error(`Local ${node.name} had no parent`);
|
|
3900
|
+
}
|
|
3901
|
+
const decl = results[0].results[0];
|
|
3902
|
+
if (parent.type === "FunctionDeclaration" ||
|
|
3903
|
+
decl.type !== "VariableDeclarator") {
|
|
3904
|
+
// we can't optimize away function or catch parameters
|
|
3905
|
+
return [];
|
|
3906
|
+
}
|
|
3907
|
+
if (parent.type !== "BlockStatement") {
|
|
3908
|
+
throw new Error(`Local ${node.name} was not declared at block scope(??)`);
|
|
3909
|
+
}
|
|
3910
|
+
decl.used = true;
|
|
3911
|
+
}
|
|
3323
3912
|
}
|
|
3324
3913
|
}
|
|
3325
3914
|
if (hasProperty(state.index, node.name)) {
|
|
3326
3915
|
if (!lookupAndReplace(node)) {
|
|
3327
|
-
state.
|
|
3916
|
+
state.usedByName[node.name] = true;
|
|
3328
3917
|
}
|
|
3329
3918
|
}
|
|
3330
3919
|
return [];
|
|
3331
3920
|
}
|
|
3332
|
-
case "MemberExpression":
|
|
3333
|
-
|
|
3334
|
-
|
|
3921
|
+
case "MemberExpression": {
|
|
3922
|
+
const property = isLookupCandidate(node);
|
|
3923
|
+
if (property) {
|
|
3924
|
+
if (hasProperty(state.index, property.name)) {
|
|
3335
3925
|
if (lookupAndReplace(node)) {
|
|
3336
3926
|
return false;
|
|
3337
3927
|
}
|
|
3338
3928
|
else {
|
|
3339
|
-
state.
|
|
3929
|
+
state.usedByName[property.name] = true;
|
|
3340
3930
|
}
|
|
3341
3931
|
}
|
|
3342
3932
|
// Don't optimize the property.
|
|
3343
3933
|
return ["object"];
|
|
3344
3934
|
}
|
|
3345
3935
|
break;
|
|
3936
|
+
}
|
|
3937
|
+
case "AssignmentExpression":
|
|
3938
|
+
case "UpdateExpression": {
|
|
3939
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
3940
|
+
if (lhs.type === "Identifier") {
|
|
3941
|
+
const map = topLocals().map;
|
|
3942
|
+
if (map) {
|
|
3943
|
+
if (hasProperty(map, lhs.name)) {
|
|
3944
|
+
const name = map[lhs.name];
|
|
3945
|
+
if (typeof name === "string") {
|
|
3946
|
+
lhs.name = name;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
else if (lhs.type === "MemberExpression") {
|
|
3952
|
+
state.traverse(lhs.object);
|
|
3953
|
+
if (lhs.computed) {
|
|
3954
|
+
state.traverse(lhs.property);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
return node.type === "AssignmentExpression" ? ["right"] : [];
|
|
3958
|
+
}
|
|
3346
3959
|
case "BlockStatement": {
|
|
3347
3960
|
const map = topLocals().map;
|
|
3348
3961
|
if (map) {
|
|
@@ -3358,7 +3971,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3358
3971
|
node.params &&
|
|
3359
3972
|
node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
|
|
3360
3973
|
state.localsStack.push({ node, map });
|
|
3361
|
-
const [parent] = state.stack.slice(-2);
|
|
3974
|
+
const [parent, self] = state.stack.slice(-2);
|
|
3975
|
+
if (state.currentFunction) {
|
|
3976
|
+
throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
|
|
3977
|
+
}
|
|
3978
|
+
state.currentFunction = self;
|
|
3362
3979
|
if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
|
|
3363
3980
|
let used = false;
|
|
3364
3981
|
if (node.id.name == "initialize") {
|
|
@@ -3385,10 +4002,23 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3385
4002
|
return replace(opt, node);
|
|
3386
4003
|
}
|
|
3387
4004
|
switch (node.type) {
|
|
4005
|
+
case "FunctionDeclaration":
|
|
4006
|
+
if (!state.currentFunction) {
|
|
4007
|
+
throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
|
|
4008
|
+
}
|
|
4009
|
+
state.currentFunction.info = state.currentFunction.next_info;
|
|
4010
|
+
delete state.currentFunction.next_info;
|
|
4011
|
+
delete state.currentFunction;
|
|
4012
|
+
break;
|
|
3388
4013
|
case "BlockStatement":
|
|
3389
4014
|
if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
|
|
3390
4015
|
node.body.splice(0, 1, ...node.body[0].body);
|
|
3391
4016
|
}
|
|
4017
|
+
// fall through
|
|
4018
|
+
case "ForStatement":
|
|
4019
|
+
if (locals.map) {
|
|
4020
|
+
cleanupUnusedVars(state, node);
|
|
4021
|
+
}
|
|
3392
4022
|
break;
|
|
3393
4023
|
case "ConditionalExpression":
|
|
3394
4024
|
case "IfStatement":
|
|
@@ -3421,17 +4051,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3421
4051
|
return replace(optimizeCall(state, node.argument, node), node.argument);
|
|
3422
4052
|
}
|
|
3423
4053
|
break;
|
|
4054
|
+
case "NewExpression":
|
|
4055
|
+
if (state.currentFunction) {
|
|
4056
|
+
const [, results] = state.lookup(node.callee);
|
|
4057
|
+
if (results) {
|
|
4058
|
+
recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
|
|
4059
|
+
}
|
|
4060
|
+
else {
|
|
4061
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
break;
|
|
3424
4065
|
case "CallExpression": {
|
|
3425
4066
|
return replace(optimizeCall(state, node, null), node);
|
|
3426
4067
|
}
|
|
3427
|
-
case "AssignmentExpression":
|
|
3428
|
-
if (node.operator === "=" &&
|
|
3429
|
-
node.left.type === "Identifier" &&
|
|
3430
|
-
node.right.type === "Identifier" &&
|
|
3431
|
-
node.left.name === node.right.name) {
|
|
3432
|
-
return { type: "Literal", value: null, raw: "null" };
|
|
3433
|
-
}
|
|
3434
|
-
break;
|
|
3435
4068
|
case "VariableDeclaration": {
|
|
3436
4069
|
const locals = topLocals();
|
|
3437
4070
|
if (locals.map &&
|
|
@@ -3444,10 +4077,10 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3444
4077
|
while (i < node.declarations.length) {
|
|
3445
4078
|
const decl = declarations[i++];
|
|
3446
4079
|
if (decl.init && decl.init.type === "CallExpression") {
|
|
3447
|
-
const inlined = optimizeCall(state, decl.init, decl);
|
|
4080
|
+
const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
|
|
3448
4081
|
if (!inlined)
|
|
3449
4082
|
continue;
|
|
3450
|
-
if (inlined.type != "BlockStatement") {
|
|
4083
|
+
if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
|
|
3451
4084
|
throw new Error("Unexpected inlined result");
|
|
3452
4085
|
}
|
|
3453
4086
|
if (!results) {
|
|
@@ -3505,6 +4138,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3505
4138
|
}
|
|
3506
4139
|
}
|
|
3507
4140
|
break;
|
|
4141
|
+
case "AssignmentExpression":
|
|
4142
|
+
if (node.operator === "=" &&
|
|
4143
|
+
node.left.type === "Identifier" &&
|
|
4144
|
+
node.right.type === "Identifier" &&
|
|
4145
|
+
node.left.name === node.right.name) {
|
|
4146
|
+
return { type: "Literal", value: null, raw: "null" };
|
|
4147
|
+
}
|
|
4148
|
+
// fall through;
|
|
4149
|
+
case "UpdateExpression":
|
|
4150
|
+
if (state.currentFunction) {
|
|
4151
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
4152
|
+
const [, results] = state.lookup(lhs);
|
|
4153
|
+
if (results) {
|
|
4154
|
+
recordModifiedDecls(state.currentFunction, results);
|
|
4155
|
+
}
|
|
4156
|
+
else {
|
|
4157
|
+
const id = lhs.type === "Identifier" ? lhs : isLookupCandidate(lhs);
|
|
4158
|
+
if (id) {
|
|
4159
|
+
recordModifiedName(state.currentFunction, id.name);
|
|
4160
|
+
}
|
|
4161
|
+
else {
|
|
4162
|
+
recordModifiedUnknown(state.currentFunction);
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
break;
|
|
3508
4167
|
}
|
|
3509
4168
|
return null;
|
|
3510
4169
|
};
|
|
@@ -3512,13 +4171,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3512
4171
|
collectNamespaces(f.ast, state);
|
|
3513
4172
|
});
|
|
3514
4173
|
state.calledFunctions = {};
|
|
3515
|
-
state.exposed =
|
|
4174
|
+
state.exposed = state.nextExposed;
|
|
4175
|
+
state.nextExposed = {};
|
|
3516
4176
|
Object.values(fnMap).forEach((f) => {
|
|
3517
4177
|
collectNamespaces(f.ast, state);
|
|
3518
4178
|
});
|
|
4179
|
+
state.exposed = state.nextExposed;
|
|
4180
|
+
state.nextExposed = {};
|
|
3519
4181
|
delete state.pre;
|
|
3520
4182
|
delete state.post;
|
|
3521
|
-
state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
|
|
4183
|
+
Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
|
|
3522
4184
|
const cleanup = (node) => {
|
|
3523
4185
|
switch (node.type) {
|
|
3524
4186
|
case "ThisExpression":
|
|
@@ -3528,7 +4190,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3528
4190
|
if (node.members.every((m) => {
|
|
3529
4191
|
const name = "name" in m ? m.name : m.id.name;
|
|
3530
4192
|
return (hasProperty(state.index, name) &&
|
|
3531
|
-
!hasProperty(state.exposed, name)
|
|
4193
|
+
!hasProperty(state.exposed, name) &&
|
|
4194
|
+
!hasProperty(state.usedByName, name));
|
|
3532
4195
|
})) {
|
|
3533
4196
|
node.enumType = [
|
|
3534
4197
|
...new Set(node.members.map((m) => {
|
|
@@ -3571,7 +4234,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3571
4234
|
case "VariableDeclaration": {
|
|
3572
4235
|
node.declarations = node.declarations.filter((d) => {
|
|
3573
4236
|
const name = variableDeclarationName(d.id);
|
|
3574
|
-
return (!hasProperty(state.index, name) ||
|
|
4237
|
+
return (!hasProperty(state.index, name) ||
|
|
4238
|
+
hasProperty(state.exposed, name) ||
|
|
4239
|
+
hasProperty(state.usedByName, name));
|
|
3575
4240
|
});
|
|
3576
4241
|
if (!node.declarations.length) {
|
|
3577
4242
|
return false;
|
|
@@ -3604,7 +4269,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3604
4269
|
}
|
|
3605
4270
|
return null;
|
|
3606
4271
|
};
|
|
3607
|
-
Object.
|
|
4272
|
+
Object.entries(fnMap).forEach(([name, f]) => {
|
|
3608
4273
|
traverseAst(f.ast, undefined, (node) => {
|
|
3609
4274
|
const ret = cleanup(node);
|
|
3610
4275
|
if (ret === false) {
|
|
@@ -3612,16 +4277,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
|
|
|
3612
4277
|
}
|
|
3613
4278
|
return ret;
|
|
3614
4279
|
});
|
|
4280
|
+
if (state.config && state.config.checkBuildPragmas) {
|
|
4281
|
+
pragmaChecker(state, f.ast, state.diagnostics?.[name]);
|
|
4282
|
+
}
|
|
3615
4283
|
});
|
|
3616
4284
|
return state.diagnostics;
|
|
3617
4285
|
}
|
|
3618
4286
|
function optimizeCall(state, node, context) {
|
|
3619
4287
|
const [name, results] = state.lookupNonlocal(node.callee);
|
|
3620
|
-
const callees = results
|
|
3621
|
-
results
|
|
3622
|
-
.map((r) => r.results)
|
|
3623
|
-
.flat()
|
|
3624
|
-
.filter((c) => c.type === "FunctionDeclaration");
|
|
4288
|
+
const callees = results ? findCallees(results) : null;
|
|
3625
4289
|
if (!callees || !callees.length) {
|
|
3626
4290
|
const n = name ||
|
|
3627
4291
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -3630,14 +4294,24 @@ function optimizeCall(state, node, context) {
|
|
|
3630
4294
|
"name" in node.callee.property &&
|
|
3631
4295
|
node.callee.property.name);
|
|
3632
4296
|
if (n) {
|
|
3633
|
-
state.
|
|
4297
|
+
if (hasProperty(state.allFunctions, n)) {
|
|
4298
|
+
if (state.currentFunction) {
|
|
4299
|
+
recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
|
|
4300
|
+
}
|
|
4301
|
+
state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
|
|
4302
|
+
}
|
|
3634
4303
|
}
|
|
3635
|
-
else {
|
|
3636
|
-
//
|
|
3637
|
-
//
|
|
4304
|
+
else if (state.currentFunction) {
|
|
4305
|
+
// I don't think this can happen: foo[x](args)
|
|
4306
|
+
// doesn't parse, so you can't even do things like
|
|
4307
|
+
// $.Toybox.Lang[:format]("fmt", [])
|
|
4308
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3638
4309
|
}
|
|
3639
4310
|
return null;
|
|
3640
4311
|
}
|
|
4312
|
+
if (state.currentFunction) {
|
|
4313
|
+
recordCalledFuncs(state.currentFunction, callees);
|
|
4314
|
+
}
|
|
3641
4315
|
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
3642
4316
|
const callee = callees[0].node;
|
|
3643
4317
|
if (!context &&
|
|
@@ -3848,20 +4522,18 @@ function checkOne(state, ns, decls, node, isStatic) {
|
|
|
3848
4522
|
return next ? (result ? result.concat(next) : next) : result;
|
|
3849
4523
|
}, null);
|
|
3850
4524
|
};
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
return lookupInContext(ns) || false;
|
|
3864
|
-
}
|
|
4525
|
+
const ndecls = ns[decls];
|
|
4526
|
+
if (ast_hasProperty(ndecls, node.name)) {
|
|
4527
|
+
return ndecls[node.name];
|
|
4528
|
+
}
|
|
4529
|
+
switch (ns.type) {
|
|
4530
|
+
case "ClassDeclaration":
|
|
4531
|
+
if (!isStatic) {
|
|
4532
|
+
return superChain(ns) || superChainScopes(ns) || false;
|
|
4533
|
+
}
|
|
4534
|
+
// fall through
|
|
4535
|
+
case "ModuleDeclaration":
|
|
4536
|
+
return lookupInContext(ns) || false;
|
|
3865
4537
|
}
|
|
3866
4538
|
return null;
|
|
3867
4539
|
}
|
|
@@ -3884,6 +4556,13 @@ function sameLookupDefinition(a, b) {
|
|
|
3884
4556
|
function api_sameLookupResult(a, b) {
|
|
3885
4557
|
return (0,external_util_cjs_namespaceObject.sameArrays)(a, b, sameLookupDefinition);
|
|
3886
4558
|
}
|
|
4559
|
+
function api_isLookupCandidate(node) {
|
|
4560
|
+
return node.computed
|
|
4561
|
+
? node.property.type === "UnaryExpression" &&
|
|
4562
|
+
node.property.operator === ":" &&
|
|
4563
|
+
node.property.argument
|
|
4564
|
+
: node.property.type === "Identifier" && node.property;
|
|
4565
|
+
}
|
|
3887
4566
|
/**
|
|
3888
4567
|
*
|
|
3889
4568
|
* @param state - The ProgramState
|
|
@@ -3904,9 +4583,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3904
4583
|
const stack = maybeStack || state.stack;
|
|
3905
4584
|
switch (node.type) {
|
|
3906
4585
|
case "MemberExpression": {
|
|
3907
|
-
|
|
4586
|
+
const property = api_isLookupCandidate(node);
|
|
4587
|
+
if (!property)
|
|
3908
4588
|
break;
|
|
3909
|
-
const property = node.property;
|
|
3910
4589
|
let result;
|
|
3911
4590
|
if (node.object.type === "ThisExpression") {
|
|
3912
4591
|
[, result] = lookup(state, decls, node.property, name, stack, true);
|
|
@@ -3920,6 +4599,9 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
3920
4599
|
result = results.reduce((current, lookupDef) => {
|
|
3921
4600
|
const items = lookupDef.results
|
|
3922
4601
|
.map((module) => {
|
|
4602
|
+
if (!api_isStateNode(module)) {
|
|
4603
|
+
return null;
|
|
4604
|
+
}
|
|
3923
4605
|
const res = checkOne(state, module, decls, property, false);
|
|
3924
4606
|
return res ? { parent: module, results: res } : null;
|
|
3925
4607
|
})
|
|
@@ -4004,6 +4686,8 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
|
|
|
4004
4686
|
}
|
|
4005
4687
|
function api_collectNamespaces(ast, stateIn) {
|
|
4006
4688
|
const state = (stateIn || {});
|
|
4689
|
+
if (!state.nextExposed)
|
|
4690
|
+
state.nextExposed = {};
|
|
4007
4691
|
if (!state.index)
|
|
4008
4692
|
state.index = {};
|
|
4009
4693
|
if (!state.stack) {
|
|
@@ -4039,7 +4723,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4039
4723
|
state.stackClone = () => state.stack.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
|
|
4040
4724
|
? { ...elm }
|
|
4041
4725
|
: elm);
|
|
4042
|
-
state.inType =
|
|
4726
|
+
state.inType = 0;
|
|
4043
4727
|
state.traverse = (root) => ast_traverseAst(root, (node) => {
|
|
4044
4728
|
try {
|
|
4045
4729
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
@@ -4047,6 +4731,13 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4047
4731
|
return [];
|
|
4048
4732
|
}
|
|
4049
4733
|
switch (node.type) {
|
|
4734
|
+
case "UnaryExpression":
|
|
4735
|
+
if (node.operator === ":" && !state.inType) {
|
|
4736
|
+
state.nextExposed[node.argument.name] = true;
|
|
4737
|
+
}
|
|
4738
|
+
break;
|
|
4739
|
+
case "AttributeList":
|
|
4740
|
+
return [];
|
|
4050
4741
|
case "Program":
|
|
4051
4742
|
if (state.stack.length != 1) {
|
|
4052
4743
|
throw new Error("Unexpected stack length for Program node");
|
|
@@ -4055,7 +4746,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4055
4746
|
break;
|
|
4056
4747
|
case "TypeSpecList":
|
|
4057
4748
|
case "TypeSpecPart":
|
|
4058
|
-
state.inType
|
|
4749
|
+
state.inType++;
|
|
4059
4750
|
break;
|
|
4060
4751
|
case "ImportModule":
|
|
4061
4752
|
case "Using": {
|
|
@@ -4101,6 +4792,16 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4101
4792
|
});
|
|
4102
4793
|
}
|
|
4103
4794
|
break;
|
|
4795
|
+
case "ForStatement":
|
|
4796
|
+
if (node.init && node.init.type === "VariableDeclaration") {
|
|
4797
|
+
state.stack.push({
|
|
4798
|
+
type: "BlockStatement",
|
|
4799
|
+
fullName: undefined,
|
|
4800
|
+
name: undefined,
|
|
4801
|
+
node: node,
|
|
4802
|
+
});
|
|
4803
|
+
}
|
|
4804
|
+
break;
|
|
4104
4805
|
case "BlockStatement": {
|
|
4105
4806
|
const [parent] = state.stack.slice(-1);
|
|
4106
4807
|
if (parent.node === node ||
|
|
@@ -4167,15 +4868,17 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4167
4868
|
// an EnumDeclaration doesn't create a scope, but
|
|
4168
4869
|
// it does create a type (if it has a name)
|
|
4169
4870
|
case "EnumDeclaration": {
|
|
4170
|
-
if (!node.id)
|
|
4871
|
+
if (!node.id) {
|
|
4872
|
+
state.inType++;
|
|
4171
4873
|
break;
|
|
4874
|
+
}
|
|
4172
4875
|
const [parent] = state.stack.slice(-1);
|
|
4173
4876
|
const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
|
|
4174
4877
|
node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
|
|
4175
4878
|
}
|
|
4176
4879
|
// fall through
|
|
4177
4880
|
case "TypedefDeclaration": {
|
|
4178
|
-
state.inType
|
|
4881
|
+
state.inType++;
|
|
4179
4882
|
const name = node.id.name;
|
|
4180
4883
|
const [parent] = state.stack.slice(-1);
|
|
4181
4884
|
if (!parent.type_decls)
|
|
@@ -4228,13 +4931,21 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4228
4931
|
break;
|
|
4229
4932
|
}
|
|
4230
4933
|
case "EnumStringBody": {
|
|
4231
|
-
state.inType
|
|
4934
|
+
if (state.inType !== 1) {
|
|
4935
|
+
throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${state.inType}.`);
|
|
4936
|
+
}
|
|
4937
|
+
state.inType--;
|
|
4232
4938
|
const [parent] = state.stack.slice(-1);
|
|
4233
4939
|
const values = parent.decls || (parent.decls = {});
|
|
4234
4940
|
let prev = -1;
|
|
4235
4941
|
node.members.forEach((m, i) => {
|
|
4236
4942
|
if (m.type == "Identifier") {
|
|
4237
|
-
prev
|
|
4943
|
+
if (typeof prev === "bigint") {
|
|
4944
|
+
prev += 1n;
|
|
4945
|
+
}
|
|
4946
|
+
else {
|
|
4947
|
+
prev += 1;
|
|
4948
|
+
}
|
|
4238
4949
|
m = node.members[i] = {
|
|
4239
4950
|
type: "EnumStringMember",
|
|
4240
4951
|
loc: m.loc,
|
|
@@ -4244,7 +4955,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4244
4955
|
init: {
|
|
4245
4956
|
type: "Literal",
|
|
4246
4957
|
value: prev,
|
|
4247
|
-
raw: prev.toString(),
|
|
4958
|
+
raw: prev.toString() + (typeof prev === "bigint" ? "l" : ""),
|
|
4248
4959
|
enumType: m.enumType,
|
|
4249
4960
|
loc: m.loc,
|
|
4250
4961
|
start: m.start,
|
|
@@ -4296,22 +5007,22 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4296
5007
|
if (state.post)
|
|
4297
5008
|
ret = state.post(node, state);
|
|
4298
5009
|
switch (type) {
|
|
4299
|
-
|
|
4300
|
-
// generally occur in TypeSpecLists. But do clear it for
|
|
4301
|
-
// SizedArrayExpression, since thats the only place they
|
|
4302
|
-
// happen on their own.
|
|
4303
|
-
case "SizedArrayExpression":
|
|
5010
|
+
case "TypeSpecPart":
|
|
4304
5011
|
case "TypeSpecList":
|
|
4305
5012
|
case "TypedefDeclaration":
|
|
4306
5013
|
case "EnumDeclaration":
|
|
4307
|
-
state.inType
|
|
5014
|
+
state.inType--;
|
|
4308
5015
|
break;
|
|
4309
5016
|
case "EnumStringBody":
|
|
4310
|
-
state.inType
|
|
5017
|
+
state.inType++;
|
|
4311
5018
|
break;
|
|
4312
5019
|
}
|
|
4313
5020
|
const [parent] = state.stack.slice(-1);
|
|
4314
5021
|
if (parent.node === node ||
|
|
5022
|
+
// The pre function might cause node.body to be skipped,
|
|
5023
|
+
// so we need to check here, just in case.
|
|
5024
|
+
// (this actually happens with prettier-extenison-monkeyc's
|
|
5025
|
+
// findItemsByRange)
|
|
4315
5026
|
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
4316
5027
|
delete parent.usings;
|
|
4317
5028
|
delete parent.imports;
|
|
@@ -4330,6 +5041,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
4330
5041
|
}
|
|
4331
5042
|
});
|
|
4332
5043
|
state.traverse(ast);
|
|
5044
|
+
if (state.inType) {
|
|
5045
|
+
throw new Error(`inType was non-zero on exit: ${state.inType}`);
|
|
5046
|
+
}
|
|
4333
5047
|
if (state.stack.length != 1) {
|
|
4334
5048
|
throw new Error("Invalid AST!");
|
|
4335
5049
|
}
|
|
@@ -4364,7 +5078,7 @@ function api_formatAst(node, monkeyCSource = null) {
|
|
|
4364
5078
|
// json. The parser knows to just treat the last line of the input
|
|
4365
5079
|
// as the ast itself, and the printers will find what they're
|
|
4366
5080
|
// looking for in the source.
|
|
4367
|
-
const source = (monkeyCSource || "") + "\n" +
|
|
5081
|
+
const source = (monkeyCSource || "") + "\n" + (0,prettier_plugin_monkeyc_namespaceObject.serializeMonkeyC)(node);
|
|
4368
5082
|
return external_prettier_namespaceObject.format(source, {
|
|
4369
5083
|
parser: "monkeyc-json",
|
|
4370
5084
|
plugins: [(prettier_plugin_monkeyc_default())],
|
|
@@ -4451,6 +5165,36 @@ function findUsingForNode(state, stack, i, node, isType) {
|
|
|
4451
5165
|
}
|
|
4452
5166
|
return null;
|
|
4453
5167
|
}
|
|
5168
|
+
const invokeInfo = {};
|
|
5169
|
+
const toyboxFnInfo = {};
|
|
5170
|
+
function api_getApiFunctionInfo(func) {
|
|
5171
|
+
if (func.fullName === "$.Toybox.Lang.Method.invoke" ||
|
|
5172
|
+
(func.node.params &&
|
|
5173
|
+
func.node.params.some((param) => param.type === "BinaryExpression" &&
|
|
5174
|
+
param.right.ts.some((tsp) => tsp.type === "TypeSpecPart" && tsp.callspec)))) {
|
|
5175
|
+
if (!invokeInfo.calledFuncs) {
|
|
5176
|
+
invokeInfo.modifiedDecls = new Set();
|
|
5177
|
+
invokeInfo.calledFuncs = new Set();
|
|
5178
|
+
invokeInfo.callsExposed = true;
|
|
5179
|
+
}
|
|
5180
|
+
if (func.name === "initialize") {
|
|
5181
|
+
const top = func.stack[func.stack.length - 1];
|
|
5182
|
+
if (top.type === "ClassDeclaration") {
|
|
5183
|
+
top.hasInvoke = true;
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
return invokeInfo;
|
|
5187
|
+
}
|
|
5188
|
+
if (!toyboxFnInfo.calledFuncs) {
|
|
5189
|
+
toyboxFnInfo.modifiedDecls = new Set();
|
|
5190
|
+
toyboxFnInfo.calledFuncs = new Set();
|
|
5191
|
+
toyboxFnInfo.resolvedDecls = new Set();
|
|
5192
|
+
}
|
|
5193
|
+
return toyboxFnInfo;
|
|
5194
|
+
}
|
|
5195
|
+
function api_markInvokeClassMethod(func) {
|
|
5196
|
+
func.info = invokeInfo;
|
|
5197
|
+
}
|
|
4454
5198
|
|
|
4455
5199
|
})();
|
|
4456
5200
|
|