@markw65/monkeyc-optimizer 1.1.7 → 1.1.8
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 +12 -0
- package/build/api.cjs +3230 -166
- package/build/optimizer.cjs +52 -48
- package/build/sdk-util.cjs +25 -1
- package/build/src/api.d.ts +17 -1
- package/build/src/optimizer-types.d.ts +12 -8
- package/build/src/sdk-util.d.ts +5 -0
- package/build/src/type-flow/types.d.ts +2 -3
- package/build/src/type-flow.d.ts +1 -1
- package/build/src/visitor.d.ts +1 -1
- package/package.json +1 -1
package/build/api.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
0 && (module.exports = {checkCompilerVersion,collectNamespaces,diagnostic,diagnosticHelper,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,getSuperClasses,hasProperty,isLocal,isLookupCandidate,isStateNode,lookupNext,markInvokeClassMethod,parseSdkVersion,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visit_resources,visitorNode});
|
|
1
|
+
0 && (module.exports = {checkCompilerVersion,collectNamespaces,createDocumentationMap,diagnostic,diagnosticHelper,findNamesInScope,findUsingForNode,formatAst,formatAstLongLines,getApiFunctionInfo,getApiMapping,getSuperClasses,hasProperty,isLocal,isLookupCandidate,isStateNode,lookupByFullName,lookupNext,lookupResultContains,lookupWithType,makeToyboxLink,mapVarDeclsByType,markInvokeClassMethod,parseSdkVersion,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visit_resources,visitorNode});
|
|
2
2
|
/******/ (() => { // webpackBootstrap
|
|
3
3
|
/******/ var __webpack_modules__ = ({
|
|
4
4
|
|
|
@@ -1372,7 +1372,7 @@ function buildDataFlowGraph(state, func, wantsLiteral, trackInsertionPoints, wan
|
|
|
1372
1372
|
});
|
|
1373
1373
|
}
|
|
1374
1374
|
if (wantsAllRefs) {
|
|
1375
|
-
const scope = state.
|
|
1375
|
+
const scope = state.top().sn;
|
|
1376
1376
|
if (scope.node === node &&
|
|
1377
1377
|
scope.type === "BlockStatement" &&
|
|
1378
1378
|
scope.decls &&
|
|
@@ -1934,9 +1934,9 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
1934
1934
|
// if decl is a local, it also can't be changed
|
|
1935
1935
|
// by a call to another function.
|
|
1936
1936
|
for (let i = 0;; i++) {
|
|
1937
|
-
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
1937
|
+
if (!state.stack[i] || decl.stack[i]?.sn !== state.stack[i].sn)
|
|
1938
1938
|
return false;
|
|
1939
|
-
if (state.stack[i].type === "FunctionDeclaration")
|
|
1939
|
+
if (state.stack[i].sn.type === "FunctionDeclaration")
|
|
1940
1940
|
return true;
|
|
1941
1941
|
}
|
|
1942
1942
|
}
|
|
@@ -2073,7 +2073,7 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
2073
2073
|
}
|
|
2074
2074
|
return null;
|
|
2075
2075
|
};
|
|
2076
|
-
state.stack = func.stack.concat(func);
|
|
2076
|
+
state.stack = func.stack.concat({ sn: func });
|
|
2077
2077
|
state.traverse(func.node.body);
|
|
2078
2078
|
}
|
|
2079
2079
|
finally {
|
|
@@ -2184,7 +2184,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
2184
2184
|
// lookup determines static-ness of the lookup context based on seeing
|
|
2185
2185
|
// a static FunctionDeclaration, but the FunctionDeclaration's stack
|
|
2186
2186
|
// doesn't include the FunctionDeclaration itself.
|
|
2187
|
-
const lookupStack = func.stack.concat(func);
|
|
2187
|
+
const lookupStack = func.stack.concat({ sn: func });
|
|
2188
2188
|
try {
|
|
2189
2189
|
state.pre = (node) => {
|
|
2190
2190
|
if (failed)
|
|
@@ -2574,10 +2574,11 @@ function inlineFunction(state, func, call, context) {
|
|
|
2574
2574
|
if (!typecheckFalse) {
|
|
2575
2575
|
return ret;
|
|
2576
2576
|
}
|
|
2577
|
-
const
|
|
2578
|
-
if (!
|
|
2577
|
+
const callerElem = state.stack.find((elem) => elem.sn.type === "FunctionDeclaration");
|
|
2578
|
+
if (!callerElem) {
|
|
2579
2579
|
return ret;
|
|
2580
2580
|
}
|
|
2581
|
+
const callerSn = callerElem.sn;
|
|
2581
2582
|
const caller = callerSn.node;
|
|
2582
2583
|
if (!caller.attrs) {
|
|
2583
2584
|
caller.attrs = withLoc({
|
|
@@ -2697,6 +2698,1122 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
2697
2698
|
}
|
|
2698
2699
|
|
|
2699
2700
|
|
|
2701
|
+
/***/ }),
|
|
2702
|
+
|
|
2703
|
+
/***/ 5530:
|
|
2704
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
2705
|
+
|
|
2706
|
+
"use strict";
|
|
2707
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
2708
|
+
/* harmony export */ "BJ": () => (/* binding */ analyze)
|
|
2709
|
+
/* harmony export */ });
|
|
2710
|
+
/* unused harmony exports getFileSources, getFileASTs, reportMissingSymbols, getLiteralFromDecls, optimizeMonkeyC */
|
|
2711
|
+
/* harmony import */ var _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9642);
|
|
2712
|
+
/* harmony import */ var _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0__);
|
|
2713
|
+
/* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(560);
|
|
2714
|
+
/* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs_promises__WEBPACK_IMPORTED_MODULE_1__);
|
|
2715
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6817);
|
|
2716
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_2__);
|
|
2717
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6652);
|
|
2718
|
+
/* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(819);
|
|
2719
|
+
/* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(333);
|
|
2720
|
+
/* harmony import */ var _optimizer_types__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9697);
|
|
2721
|
+
/* harmony import */ var _pragma_checker__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(1851);
|
|
2722
|
+
/* harmony import */ var _pre__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(9910);
|
|
2723
|
+
/* harmony import */ var _type_flow__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(4859);
|
|
2724
|
+
/* harmony import */ var _type_flow_could_be__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(4055);
|
|
2725
|
+
/* harmony import */ var _type_flow_interp__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(7161);
|
|
2726
|
+
/* harmony import */ var _type_flow_optimize__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(3687);
|
|
2727
|
+
/* harmony import */ var _type_flow_sub_type__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(9234);
|
|
2728
|
+
/* harmony import */ var _unused_exprs__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(424);
|
|
2729
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(6906);
|
|
2730
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_15__);
|
|
2731
|
+
/* harmony import */ var _variable_renamer__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(4405);
|
|
2732
|
+
|
|
2733
|
+
|
|
2734
|
+
|
|
2735
|
+
|
|
2736
|
+
|
|
2737
|
+
|
|
2738
|
+
|
|
2739
|
+
|
|
2740
|
+
|
|
2741
|
+
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
|
|
2745
|
+
|
|
2746
|
+
|
|
2747
|
+
|
|
2748
|
+
|
|
2749
|
+
/*
|
|
2750
|
+
* Map each name to the list of StateNodes that declare that
|
|
2751
|
+
* name (excluding Functions, which are already in allFunctions)
|
|
2752
|
+
*/
|
|
2753
|
+
function collectDeclarationsByName(state) {
|
|
2754
|
+
state.allDeclarations = {};
|
|
2755
|
+
const allDecls = state.allDeclarations;
|
|
2756
|
+
const helper = (sn) => {
|
|
2757
|
+
if (sn.type === "ClassDeclaration" ||
|
|
2758
|
+
sn.type === "Program" ||
|
|
2759
|
+
sn.type === "ModuleDeclaration") {
|
|
2760
|
+
if (sn.decls) {
|
|
2761
|
+
Object.entries(sn.decls).forEach(([key, decls]) => {
|
|
2762
|
+
const keyed = [];
|
|
2763
|
+
decls.forEach((decl) => {
|
|
2764
|
+
switch (decl.type) {
|
|
2765
|
+
case "ClassDeclaration":
|
|
2766
|
+
case "ModuleDeclaration":
|
|
2767
|
+
helper(decl);
|
|
2768
|
+
// fall through;
|
|
2769
|
+
case "VariableDeclarator":
|
|
2770
|
+
case "EnumStringMember":
|
|
2771
|
+
case "FunctionDeclaration":
|
|
2772
|
+
keyed.push(sn);
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
if (keyed.length) {
|
|
2776
|
+
if (!(0,_api__WEBPACK_IMPORTED_MODULE_2__.hasProperty)(allDecls, key)) {
|
|
2777
|
+
allDecls[key] = keyed;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
allDecls[key].push(...keyed);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
helper(state.stack[0].sn);
|
|
2788
|
+
}
|
|
2789
|
+
function collectClassInfo(state) {
|
|
2790
|
+
const toybox = state.stack[0].sn.decls["Toybox"][0];
|
|
2791
|
+
const lang = toybox.decls["Lang"][0];
|
|
2792
|
+
const object = lang.decls["Object"];
|
|
2793
|
+
state.allClasses.forEach((elm) => {
|
|
2794
|
+
if (elm.stack[elm.stack.length - 1].sn.type === "ClassDeclaration") {
|
|
2795
|
+
// nested classes don't get access to their contained
|
|
2796
|
+
// context. Put them in the global scope instead.
|
|
2797
|
+
elm.stack = elm.stack.slice(0, 1);
|
|
2798
|
+
}
|
|
2799
|
+
if (elm.node.superClass) {
|
|
2800
|
+
const [name, lookupDefns] = state.lookup(elm.node.superClass, null, elm.stack);
|
|
2801
|
+
const superClass = lookupDefns &&
|
|
2802
|
+
lookupDefns
|
|
2803
|
+
.map((lookupDefn) => lookupDefn.results)
|
|
2804
|
+
.flat()
|
|
2805
|
+
.filter((c) => (0,_api__WEBPACK_IMPORTED_MODULE_2__.isStateNode)(c) && c.type === "ClassDeclaration");
|
|
2806
|
+
// set it "true" if there is a superClass, but we can't find it.
|
|
2807
|
+
elm.superClass = superClass && superClass.length ? superClass : true;
|
|
2808
|
+
if (name && elm.superClass !== true) {
|
|
2809
|
+
/*
|
|
2810
|
+
* The runtime behavior of monkeyc is strange. Lookups
|
|
2811
|
+
* of the name of the superclass, either bare, or via self.<name>
|
|
2812
|
+
* always find the superclass, even if there's a member variable
|
|
2813
|
+
* or method of the same name. So its ok to just overwrite
|
|
2814
|
+
* elm.decls[name] here.
|
|
2815
|
+
*
|
|
2816
|
+
* ie
|
|
2817
|
+
*
|
|
2818
|
+
* class A { function foo() as Number { return 1; } }
|
|
2819
|
+
* class B { function foo() as String { return "B"; } }
|
|
2820
|
+
* class C extends A {
|
|
2821
|
+
* var A as B = new B();
|
|
2822
|
+
* function initialize() {
|
|
2823
|
+
* A.initialize(); // class A's initialize
|
|
2824
|
+
* A.foo(); // returns 1
|
|
2825
|
+
* self.A.foo(); // still returns 1
|
|
2826
|
+
* }
|
|
2827
|
+
* }
|
|
2828
|
+
*
|
|
2829
|
+
* The typechecker seems to get confused in some circumstances
|
|
2830
|
+
* though (ie it doesn't always use the same rules)
|
|
2831
|
+
*/
|
|
2832
|
+
if (!elm.decls)
|
|
2833
|
+
elm.decls = {};
|
|
2834
|
+
elm.decls[name] = elm.superClass;
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
else if (elm !== object[0]) {
|
|
2838
|
+
elm.superClass = object;
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
const markOverrides = (cls, scls) => {
|
|
2842
|
+
if (scls === true)
|
|
2843
|
+
return;
|
|
2844
|
+
scls.forEach((c) => {
|
|
2845
|
+
c.decls &&
|
|
2846
|
+
Object.values(c.decls).forEach((funcs) => {
|
|
2847
|
+
funcs.forEach((f) => {
|
|
2848
|
+
if (f.type === "FunctionDeclaration" &&
|
|
2849
|
+
(0,_api__WEBPACK_IMPORTED_MODULE_2__.hasProperty)(cls.decls, f.name)) {
|
|
2850
|
+
f.node.hasOverride = true;
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
});
|
|
2854
|
+
if (c.superClass)
|
|
2855
|
+
markOverrides(cls, c.superClass);
|
|
2856
|
+
});
|
|
2857
|
+
};
|
|
2858
|
+
state.allClasses.forEach((elm) => {
|
|
2859
|
+
if (elm.superClass)
|
|
2860
|
+
markOverrides(elm, elm.superClass);
|
|
2861
|
+
if (elm.hasInvoke && elm.decls) {
|
|
2862
|
+
Object.values(elm.decls).forEach((funcs) => {
|
|
2863
|
+
funcs.forEach((f) => {
|
|
2864
|
+
if (f.type === "FunctionDeclaration" &&
|
|
2865
|
+
!(f.attributes & _optimizer_types__WEBPACK_IMPORTED_MODULE_6__/* .StateNodeAttributes.STATIC */ .m.STATIC)) {
|
|
2866
|
+
(0,_api__WEBPACK_IMPORTED_MODULE_2__.markInvokeClassMethod)(state, f);
|
|
2867
|
+
}
|
|
2868
|
+
});
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
function getFileSources(fnMap) {
|
|
2874
|
+
return Promise.all(Object.entries(fnMap).map(([name, value]) => {
|
|
2875
|
+
return (value.monkeyCSource ||
|
|
2876
|
+
fs_promises__WEBPACK_IMPORTED_MODULE_1__.readFile(name)
|
|
2877
|
+
.then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
|
|
2878
|
+
})).then(() => {
|
|
2879
|
+
return;
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
function getFileASTs(fnMap) {
|
|
2883
|
+
return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
|
|
2884
|
+
if (!value.ast) {
|
|
2885
|
+
try {
|
|
2886
|
+
value.ast = _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0___default().parsers.monkeyc.parse(value.monkeyCSource, null, {
|
|
2887
|
+
filepath: name,
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
catch (e) {
|
|
2891
|
+
ok = false;
|
|
2892
|
+
if (e instanceof Error) {
|
|
2893
|
+
value.parserError = e;
|
|
2894
|
+
}
|
|
2895
|
+
else {
|
|
2896
|
+
value.parserError = new Error("An unknown parser error occurred");
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return ok;
|
|
2901
|
+
}, true));
|
|
2902
|
+
}
|
|
2903
|
+
async function analyze(fnMap, resourcesMap, manifestXML, config) {
|
|
2904
|
+
let hasTests = false;
|
|
2905
|
+
let markApi = true;
|
|
2906
|
+
const preState = {
|
|
2907
|
+
fnMap,
|
|
2908
|
+
config,
|
|
2909
|
+
allFunctions: {},
|
|
2910
|
+
allClasses: [],
|
|
2911
|
+
shouldExclude(node) {
|
|
2912
|
+
if ("attrs" in node &&
|
|
2913
|
+
node.attrs &&
|
|
2914
|
+
"attributes" in node.attrs &&
|
|
2915
|
+
node.attrs.attributes &&
|
|
2916
|
+
node.loc?.source) {
|
|
2917
|
+
const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
|
|
2918
|
+
if (excludeAnnotations) {
|
|
2919
|
+
return node.attrs.attributes.elements.reduce((drop, attr) => {
|
|
2920
|
+
if (attr.type !== "UnaryExpression")
|
|
2921
|
+
return drop;
|
|
2922
|
+
if (attr.argument.type !== "Identifier")
|
|
2923
|
+
return drop;
|
|
2924
|
+
if ((0,_api__WEBPACK_IMPORTED_MODULE_2__.hasProperty)(excludeAnnotations, attr.argument.name)) {
|
|
2925
|
+
return true;
|
|
2926
|
+
}
|
|
2927
|
+
if (attr.argument.name === "test") {
|
|
2928
|
+
hasTests = true;
|
|
2929
|
+
}
|
|
2930
|
+
return drop;
|
|
2931
|
+
}, false);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
return false;
|
|
2935
|
+
},
|
|
2936
|
+
pre(node, state) {
|
|
2937
|
+
switch (node.type) {
|
|
2938
|
+
case "FunctionDeclaration":
|
|
2939
|
+
case "ModuleDeclaration":
|
|
2940
|
+
case "ClassDeclaration": {
|
|
2941
|
+
const scope = state.top().sn;
|
|
2942
|
+
scope.stack = state.stackClone().slice(0, -1);
|
|
2943
|
+
if (scope.type === "FunctionDeclaration") {
|
|
2944
|
+
if (markApi) {
|
|
2945
|
+
node.body = null;
|
|
2946
|
+
scope.info = (0,_api__WEBPACK_IMPORTED_MODULE_2__.getApiFunctionInfo)(state, scope);
|
|
2947
|
+
}
|
|
2948
|
+
const allFuncs = state.allFunctions;
|
|
2949
|
+
if (!(0,_api__WEBPACK_IMPORTED_MODULE_2__.hasProperty)(allFuncs, scope.name)) {
|
|
2950
|
+
allFuncs[scope.name] = [scope];
|
|
2951
|
+
}
|
|
2952
|
+
else {
|
|
2953
|
+
allFuncs[scope.name].push(scope);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
else if (scope.type === "ClassDeclaration") {
|
|
2957
|
+
state.allClasses.push(scope);
|
|
2958
|
+
}
|
|
2959
|
+
break;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
return null;
|
|
2963
|
+
},
|
|
2964
|
+
};
|
|
2965
|
+
await (0,_api__WEBPACK_IMPORTED_MODULE_2__.getApiMapping)(preState, resourcesMap, manifestXML);
|
|
2966
|
+
markApi = false;
|
|
2967
|
+
const state = preState;
|
|
2968
|
+
await getFileASTs(fnMap);
|
|
2969
|
+
Object.entries(fnMap).forEach(([name, value]) => {
|
|
2970
|
+
const { ast, parserError } = value;
|
|
2971
|
+
if (!ast) {
|
|
2972
|
+
throw parserError || new Error(`Failed to parse ${name}`);
|
|
2973
|
+
}
|
|
2974
|
+
hasTests = false;
|
|
2975
|
+
(0,_api__WEBPACK_IMPORTED_MODULE_2__.collectNamespaces)(ast, state);
|
|
2976
|
+
value.hasTests = hasTests;
|
|
2977
|
+
});
|
|
2978
|
+
delete state.shouldExclude;
|
|
2979
|
+
delete state.pre;
|
|
2980
|
+
collectDeclarationsByName(state);
|
|
2981
|
+
collectClassInfo(state);
|
|
2982
|
+
state.exposed = state.nextExposed;
|
|
2983
|
+
state.nextExposed = {};
|
|
2984
|
+
return state;
|
|
2985
|
+
}
|
|
2986
|
+
function reportMissingSymbols(state, config) {
|
|
2987
|
+
const diagnosticType = config?.checkInvalidSymbols !== "OFF"
|
|
2988
|
+
? config?.checkInvalidSymbols || "WARNING"
|
|
2989
|
+
: null;
|
|
2990
|
+
const compiler2DiagnosticType = config?.checkCompilerLookupRules !== "OFF"
|
|
2991
|
+
? config?.checkCompilerLookupRules || "WARNING"
|
|
2992
|
+
: null;
|
|
2993
|
+
if (diagnosticType &&
|
|
2994
|
+
!config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
|
|
2995
|
+
const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
|
|
2996
|
+
const report = (ast) => {
|
|
2997
|
+
visitReferences(state, ast, null, false, (node, results, error) => {
|
|
2998
|
+
if (node.type === "BinaryExpression" && node.operator === "has") {
|
|
2999
|
+
// Its not an error to check whether a property exists...
|
|
3000
|
+
return undefined;
|
|
3001
|
+
}
|
|
3002
|
+
if (!error) {
|
|
3003
|
+
if (state.sdkVersion === 4001006 &&
|
|
3004
|
+
compiler2DiagnosticType &&
|
|
3005
|
+
node.type === "MemberExpression" &&
|
|
3006
|
+
(node.object.type === "Identifier" ||
|
|
3007
|
+
node.object.type === "MemberExpression") &&
|
|
3008
|
+
results.some((result) => {
|
|
3009
|
+
const parent = result.parent;
|
|
3010
|
+
if (!parent || parent.type !== "ClassDeclaration") {
|
|
3011
|
+
return false;
|
|
3012
|
+
}
|
|
3013
|
+
return result.results.some((sn) => {
|
|
3014
|
+
switch (sn.type) {
|
|
3015
|
+
case "VariableDeclarator":
|
|
3016
|
+
case "FunctionDeclaration":
|
|
3017
|
+
return (sn.attributes &
|
|
3018
|
+
(StateNodeAttributes.PRIVATE |
|
|
3019
|
+
StateNodeAttributes.PROTECTED));
|
|
3020
|
+
}
|
|
3021
|
+
return false;
|
|
3022
|
+
});
|
|
3023
|
+
})) {
|
|
3024
|
+
diagnostic(state, node, `The expression ${formatAst(node)} will fail at runtime using sdk-4.1.6`, compiler2DiagnosticType);
|
|
3025
|
+
}
|
|
3026
|
+
return undefined;
|
|
3027
|
+
}
|
|
3028
|
+
let nodeStr;
|
|
3029
|
+
if (state.inType) {
|
|
3030
|
+
if (!checkTypes || (nodeStr = formatAst(node)).match(/^Void|Null$/)) {
|
|
3031
|
+
return undefined;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
diagnostic(state, node, `Undefined symbol ${nodeStr || formatAst(node)}`, diagnosticType);
|
|
3035
|
+
return false;
|
|
3036
|
+
});
|
|
3037
|
+
};
|
|
3038
|
+
Object.values(state.fnMap).forEach((v) => v.ast && report(v.ast));
|
|
3039
|
+
state.rezAst && report(state.rezAst);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
function compareLiteralLike(a, b) {
|
|
3043
|
+
while (a.type === "BinaryExpression")
|
|
3044
|
+
a = a.left;
|
|
3045
|
+
while (b.type === "BinaryExpression")
|
|
3046
|
+
b = b.left;
|
|
3047
|
+
return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
|
|
3048
|
+
}
|
|
3049
|
+
function getLiteralFromDecls(lookupDefns) {
|
|
3050
|
+
if (!lookupDefns.length)
|
|
3051
|
+
return null;
|
|
3052
|
+
let result = null;
|
|
3053
|
+
if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
|
|
3054
|
+
if (d.type === "EnumStringMember" ||
|
|
3055
|
+
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
3056
|
+
const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
|
|
3057
|
+
if (!init)
|
|
3058
|
+
return false;
|
|
3059
|
+
if (!result) {
|
|
3060
|
+
result = init;
|
|
3061
|
+
return true;
|
|
3062
|
+
}
|
|
3063
|
+
else {
|
|
3064
|
+
return compareLiteralLike(init, result);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
return false;
|
|
3068
|
+
}))) {
|
|
3069
|
+
return result;
|
|
3070
|
+
}
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
function optimizeNode(istate, node) {
|
|
3074
|
+
if (istate.state.inlining)
|
|
3075
|
+
return null;
|
|
3076
|
+
if (istate.state.inType) {
|
|
3077
|
+
return null;
|
|
3078
|
+
}
|
|
3079
|
+
switch (node.type) {
|
|
3080
|
+
case "UpdateExpression":
|
|
3081
|
+
// we only evaluated any subexpressions of the argument.
|
|
3082
|
+
evaluateNode(istate, node.argument);
|
|
3083
|
+
break;
|
|
3084
|
+
case "AssignmentExpression": {
|
|
3085
|
+
// we only evaluated any subexpressions of the lhs.
|
|
3086
|
+
const right = istate.stack.pop();
|
|
3087
|
+
evaluateNode(istate, node.left);
|
|
3088
|
+
istate.stack.push(right);
|
|
3089
|
+
break;
|
|
3090
|
+
}
|
|
3091
|
+
case "BinaryExpression":
|
|
3092
|
+
if (node.operator === "has" &&
|
|
3093
|
+
node.right.type === "UnaryExpression" &&
|
|
3094
|
+
node.right.operator === ":") {
|
|
3095
|
+
// we skipped this node, so evaluate it now...
|
|
3096
|
+
istate.stack.push(evaluate(istate, node.right));
|
|
3097
|
+
}
|
|
3098
|
+
break;
|
|
3099
|
+
}
|
|
3100
|
+
const before = beforeEvaluate(istate, node);
|
|
3101
|
+
if (before != null)
|
|
3102
|
+
return before;
|
|
3103
|
+
evaluateNode(istate, node);
|
|
3104
|
+
return afterEvaluate(istate, node);
|
|
3105
|
+
}
|
|
3106
|
+
function evaluateFunction(istate, func, args) {
|
|
3107
|
+
if (!func.body ||
|
|
3108
|
+
istate.state.inlining ||
|
|
3109
|
+
(args && args.length !== func.params.length)) {
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
const paramValues = args &&
|
|
3113
|
+
Object.fromEntries(func.params.map((p, i) => [
|
|
3114
|
+
variableDeclarationName(p),
|
|
3115
|
+
args[i],
|
|
3116
|
+
]));
|
|
3117
|
+
let ret = null;
|
|
3118
|
+
const body = args ? cloneDeep(func.body) : func.body;
|
|
3119
|
+
const depth = istate.stack.length;
|
|
3120
|
+
try {
|
|
3121
|
+
traverseAst(body, (node) => {
|
|
3122
|
+
switch (node.type) {
|
|
3123
|
+
case "BlockStatement":
|
|
3124
|
+
case "ReturnStatement":
|
|
3125
|
+
case "UnaryExpression":
|
|
3126
|
+
case "BinaryExpression":
|
|
3127
|
+
case "Literal":
|
|
3128
|
+
case "Identifier":
|
|
3129
|
+
return;
|
|
3130
|
+
default:
|
|
3131
|
+
throw new Error("Bad node type");
|
|
3132
|
+
}
|
|
3133
|
+
}, !args
|
|
3134
|
+
? undefined
|
|
3135
|
+
: (node) => {
|
|
3136
|
+
switch (node.type) {
|
|
3137
|
+
case "ReturnStatement":
|
|
3138
|
+
ret = node.argument || null;
|
|
3139
|
+
return null;
|
|
3140
|
+
case "BlockStatement":
|
|
3141
|
+
return null;
|
|
3142
|
+
case "Identifier":
|
|
3143
|
+
if (hasProperty(paramValues, node.name)) {
|
|
3144
|
+
istate.stack.push(evaluate(istate, (node = paramValues[node.name])));
|
|
3145
|
+
return node;
|
|
3146
|
+
}
|
|
3147
|
+
// fall through;
|
|
3148
|
+
default: {
|
|
3149
|
+
const repl = optimizeNode(istate, node) || node;
|
|
3150
|
+
if (repl.type === "Literal")
|
|
3151
|
+
return repl;
|
|
3152
|
+
throw new Error("Didn't optimize");
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
});
|
|
3156
|
+
delete istate.state.inlining;
|
|
3157
|
+
istate.stack.length = depth;
|
|
3158
|
+
return ret;
|
|
3159
|
+
}
|
|
3160
|
+
catch (e) {
|
|
3161
|
+
delete istate.state.inlining;
|
|
3162
|
+
istate.stack.length = depth;
|
|
3163
|
+
return false;
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
function markFunctionCalled(state, func) {
|
|
3167
|
+
if (!hasProperty(state.calledFunctions, func.id.name)) {
|
|
3168
|
+
state.calledFunctions[func.id.name] = [func];
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
pushUnique(state.calledFunctions[func.id.name], func);
|
|
3172
|
+
}
|
|
3173
|
+
async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
3174
|
+
const state = (await analyze(fnMap, resourcesMap, manifestXML, config));
|
|
3175
|
+
state.localsStack = [{}];
|
|
3176
|
+
state.calledFunctions = {};
|
|
3177
|
+
state.usedByName = {};
|
|
3178
|
+
let again = false;
|
|
3179
|
+
const optimizeCallHelper = (istate, call, node) => {
|
|
3180
|
+
const result = optimizeCall(istate, call, node);
|
|
3181
|
+
if (result) {
|
|
3182
|
+
if (isExpression(result)) {
|
|
3183
|
+
istate.stack[istate.stack.length - 1].node = result;
|
|
3184
|
+
}
|
|
3185
|
+
again = true;
|
|
3186
|
+
}
|
|
3187
|
+
return result;
|
|
3188
|
+
};
|
|
3189
|
+
const topLocals = () => state.localsStack[state.localsStack.length - 1];
|
|
3190
|
+
/*
|
|
3191
|
+
* Might this function be called from somewhere, including
|
|
3192
|
+
* callbacks from the api (eg getSettingsView, etc).
|
|
3193
|
+
*/
|
|
3194
|
+
const maybeCalled = (func) => {
|
|
3195
|
+
if (!func.body) {
|
|
3196
|
+
// this is an api.mir function. It can be called
|
|
3197
|
+
return true;
|
|
3198
|
+
}
|
|
3199
|
+
if (hasProperty(state.exposed, func.id.name))
|
|
3200
|
+
return true;
|
|
3201
|
+
if (func.attrs &&
|
|
3202
|
+
func.attrs.attributes &&
|
|
3203
|
+
func.attrs.attributes.elements.some((attr) => {
|
|
3204
|
+
if (attr.type !== "UnaryExpression")
|
|
3205
|
+
return false;
|
|
3206
|
+
if (attr.argument.type !== "Identifier")
|
|
3207
|
+
return false;
|
|
3208
|
+
return attr.argument.name === "test";
|
|
3209
|
+
})) {
|
|
3210
|
+
return true;
|
|
3211
|
+
}
|
|
3212
|
+
if (hasProperty(state.calledFunctions, func.id.name)) {
|
|
3213
|
+
return (state.calledFunctions[func.id.name].find((f) => f === func) !== null);
|
|
3214
|
+
}
|
|
3215
|
+
return false;
|
|
3216
|
+
};
|
|
3217
|
+
/*
|
|
3218
|
+
* Does elm (a class) have a maybeCalled function called name,
|
|
3219
|
+
* anywhere in its superClass chain.
|
|
3220
|
+
*/
|
|
3221
|
+
const checkInherited = (elm, name) => elm.superClass === true ||
|
|
3222
|
+
(elm.superClass != null &&
|
|
3223
|
+
elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
|
|
3224
|
+
sc.decls[name].some((f) => isStateNode(f) &&
|
|
3225
|
+
f.type === "FunctionDeclaration" &&
|
|
3226
|
+
maybeCalled(f.node))) ||
|
|
3227
|
+
(sc.superClass && checkInherited(sc, name))));
|
|
3228
|
+
const renamer = (idnode) => {
|
|
3229
|
+
const ident = idnode.type === "Identifier" ? idnode : idnode.left;
|
|
3230
|
+
const locals = topLocals();
|
|
3231
|
+
const { map } = locals;
|
|
3232
|
+
if (map) {
|
|
3233
|
+
const declName = ident.name;
|
|
3234
|
+
const name = renameVariable(state, locals, declName);
|
|
3235
|
+
if (name) {
|
|
3236
|
+
const [, results] = state.lookupValue(ident);
|
|
3237
|
+
if (!results) {
|
|
3238
|
+
throw new Error(`Didn't find local ${declName} which needed renaming`);
|
|
3239
|
+
}
|
|
3240
|
+
if (results.length !== 1) {
|
|
3241
|
+
throw new Error(`Lookup of local ${declName} found more than one result`);
|
|
3242
|
+
}
|
|
3243
|
+
const parent = results[0].parent;
|
|
3244
|
+
if (!parent) {
|
|
3245
|
+
throw new Error(`No parent in lookup of local ${declName}`);
|
|
3246
|
+
}
|
|
3247
|
+
const decls = parent.decls;
|
|
3248
|
+
if (!decls || !hasProperty(decls, declName)) {
|
|
3249
|
+
throw new Error(`Missing decls in lookup of local ${declName}`);
|
|
3250
|
+
}
|
|
3251
|
+
if (hasProperty(decls, name)) {
|
|
3252
|
+
throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
|
|
3253
|
+
}
|
|
3254
|
+
if (decls[declName].length === 1) {
|
|
3255
|
+
decls[name] = decls[declName];
|
|
3256
|
+
delete decls[declName];
|
|
3257
|
+
}
|
|
3258
|
+
else {
|
|
3259
|
+
let i = decls[declName].length;
|
|
3260
|
+
while (i--) {
|
|
3261
|
+
const decl = decls[declName][i];
|
|
3262
|
+
if (decl === idnode ||
|
|
3263
|
+
(decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
|
|
3264
|
+
decls[declName].splice(i, 1);
|
|
3265
|
+
decls[name] = [decl];
|
|
3266
|
+
break;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
if (i < 0) {
|
|
3270
|
+
throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
ident.name = name;
|
|
3274
|
+
}
|
|
3275
|
+
else {
|
|
3276
|
+
map[declName] = true;
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
};
|
|
3280
|
+
// use this when optimizing initializer expressions,
|
|
3281
|
+
// outside of any function.
|
|
3282
|
+
const gistate = { state, stack: [] };
|
|
3283
|
+
if (state.config?.checkTypes !== "OFF" &&
|
|
3284
|
+
state.config?.trustDeclaredTypes &&
|
|
3285
|
+
state.config.propagateTypes) {
|
|
3286
|
+
gistate.typeChecker =
|
|
3287
|
+
state.config.typeCheckLevel?.toLowerCase() === "strict"
|
|
3288
|
+
? subtypeOf
|
|
3289
|
+
: couldBeWeak;
|
|
3290
|
+
gistate.checkTypes = state.config?.checkTypes || "WARNING";
|
|
3291
|
+
}
|
|
3292
|
+
// use this when type inference is enabled, and we're
|
|
3293
|
+
// inside a function.
|
|
3294
|
+
let istate = gistate;
|
|
3295
|
+
state.pre = (node) => {
|
|
3296
|
+
const ret = preEvaluate(istate, node);
|
|
3297
|
+
switch (node.type) {
|
|
3298
|
+
case "EnumDeclaration":
|
|
3299
|
+
return [];
|
|
3300
|
+
case "ForStatement": {
|
|
3301
|
+
const map = topLocals().map;
|
|
3302
|
+
if (map) {
|
|
3303
|
+
state.localsStack.push({ node, map: { ...map } });
|
|
3304
|
+
}
|
|
3305
|
+
break;
|
|
3306
|
+
}
|
|
3307
|
+
case "VariableDeclarator": {
|
|
3308
|
+
renamer(node.id);
|
|
3309
|
+
break;
|
|
3310
|
+
}
|
|
3311
|
+
case "CatchClause":
|
|
3312
|
+
if (node.param) {
|
|
3313
|
+
state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
|
|
3314
|
+
renamer(node.param);
|
|
3315
|
+
}
|
|
3316
|
+
break;
|
|
3317
|
+
case "BinaryExpression":
|
|
3318
|
+
if (node.operator === "has" &&
|
|
3319
|
+
node.right.type === "UnaryExpression" &&
|
|
3320
|
+
node.right.operator === ":") {
|
|
3321
|
+
// Using `expr has :symbol` doesn't "expose"
|
|
3322
|
+
// symbol, and the rhs of an "as" isn't an
|
|
3323
|
+
// expression. In both cases, skip the rhs
|
|
3324
|
+
return ["left"];
|
|
3325
|
+
}
|
|
3326
|
+
break;
|
|
3327
|
+
case "Identifier": {
|
|
3328
|
+
const map = topLocals().map;
|
|
3329
|
+
if (map) {
|
|
3330
|
+
if (hasProperty(map, node.name)) {
|
|
3331
|
+
const name = map[node.name];
|
|
3332
|
+
if (typeof name === "string") {
|
|
3333
|
+
node.name = name;
|
|
3334
|
+
}
|
|
3335
|
+
const [, results] = state.lookupValue(node);
|
|
3336
|
+
if (results) {
|
|
3337
|
+
if (results.length !== 1 || results[0].results.length !== 1) {
|
|
3338
|
+
throw new Error(`Local ${node.name} had multiple lookup results`);
|
|
3339
|
+
}
|
|
3340
|
+
const parent = results[0].parent;
|
|
3341
|
+
if (!parent) {
|
|
3342
|
+
throw new Error(`Local ${node.name} had no parent`);
|
|
3343
|
+
}
|
|
3344
|
+
const decl = results[0].results[0];
|
|
3345
|
+
if (parent.type === "FunctionDeclaration" ||
|
|
3346
|
+
decl.type !== "VariableDeclarator") {
|
|
3347
|
+
// we can't optimize away function or catch parameters
|
|
3348
|
+
return [];
|
|
3349
|
+
}
|
|
3350
|
+
if (parent.type !== "BlockStatement") {
|
|
3351
|
+
throw new Error(`Local ${node.name} was not declared at block scope(??)`);
|
|
3352
|
+
}
|
|
3353
|
+
decl.used = true;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
return [];
|
|
3358
|
+
}
|
|
3359
|
+
case "AssignmentExpression":
|
|
3360
|
+
case "UpdateExpression": {
|
|
3361
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
3362
|
+
if (lhs.type === "Identifier") {
|
|
3363
|
+
const map = topLocals().map;
|
|
3364
|
+
if (map) {
|
|
3365
|
+
if (hasProperty(map, lhs.name)) {
|
|
3366
|
+
const name = map[lhs.name];
|
|
3367
|
+
if (typeof name === "string") {
|
|
3368
|
+
lhs.name = name;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
else if (lhs.type === "MemberExpression") {
|
|
3374
|
+
const object = state.traverse(lhs.object);
|
|
3375
|
+
if (object) {
|
|
3376
|
+
lhs.object = object;
|
|
3377
|
+
}
|
|
3378
|
+
if (!isLookupCandidate(lhs)) {
|
|
3379
|
+
const property = state.traverse(lhs.property);
|
|
3380
|
+
if (property) {
|
|
3381
|
+
lhs.property = property;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
return node.type === "AssignmentExpression" ? ["right"] : [];
|
|
3386
|
+
}
|
|
3387
|
+
case "BlockStatement": {
|
|
3388
|
+
const map = topLocals().map;
|
|
3389
|
+
if (map) {
|
|
3390
|
+
state.localsStack.push({
|
|
3391
|
+
node,
|
|
3392
|
+
map: { ...map },
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
break;
|
|
3396
|
+
}
|
|
3397
|
+
case "FunctionDeclaration": {
|
|
3398
|
+
const map = {};
|
|
3399
|
+
node.params &&
|
|
3400
|
+
node.params.forEach((p) => (map[variableDeclarationName(p)] = true));
|
|
3401
|
+
state.localsStack.push({ node, map });
|
|
3402
|
+
const [{ sn: parent }, { sn: self }] = state.stack.slice(-2);
|
|
3403
|
+
if (state.currentFunction) {
|
|
3404
|
+
throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
|
|
3405
|
+
}
|
|
3406
|
+
state.currentFunction = self;
|
|
3407
|
+
const is = !state.config?.propagateTypes ||
|
|
3408
|
+
node.attrs?.attributes?.elements.find((attr) => attr.type === "UnaryExpression" &&
|
|
3409
|
+
attr.argument.name === "noConstProp")
|
|
3410
|
+
? null
|
|
3411
|
+
: buildTypeInfo(state, state.currentFunction, true);
|
|
3412
|
+
if (is) {
|
|
3413
|
+
/*
|
|
3414
|
+
* istate contains a copy of state, but we need the real
|
|
3415
|
+
* thing, because "state" is captured here.
|
|
3416
|
+
*
|
|
3417
|
+
* A better solution will be to separate out a
|
|
3418
|
+
* "lookup context", which will be a stack, plus a couple
|
|
3419
|
+
* of fields from state, and then pass that around.
|
|
3420
|
+
*/
|
|
3421
|
+
is.state = state;
|
|
3422
|
+
if (state.config?.checkTypes !== "OFF" &&
|
|
3423
|
+
state.config?.trustDeclaredTypes) {
|
|
3424
|
+
is.typeChecker = gistate.typeChecker;
|
|
3425
|
+
is.checkTypes = state.config?.checkTypes || "WARNING";
|
|
3426
|
+
}
|
|
3427
|
+
istate = is;
|
|
3428
|
+
}
|
|
3429
|
+
if (parent.type === "ClassDeclaration" && !maybeCalled(node)) {
|
|
3430
|
+
let used = false;
|
|
3431
|
+
if (node.id.name === "initialize") {
|
|
3432
|
+
used = true;
|
|
3433
|
+
}
|
|
3434
|
+
else if (parent.superClass) {
|
|
3435
|
+
used = checkInherited(parent, node.id.name);
|
|
3436
|
+
}
|
|
3437
|
+
if (used) {
|
|
3438
|
+
markFunctionCalled(state, node);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
// We dont want to call evaluateNode on
|
|
3442
|
+
// id, args or returnType
|
|
3443
|
+
return ["body"];
|
|
3444
|
+
}
|
|
3445
|
+
case "ClassDeclaration":
|
|
3446
|
+
case "ModuleDeclaration":
|
|
3447
|
+
// We dont want to call evaluateNode on
|
|
3448
|
+
// id, or superClass
|
|
3449
|
+
return ["body"];
|
|
3450
|
+
}
|
|
3451
|
+
return ret;
|
|
3452
|
+
};
|
|
3453
|
+
state.post = (node) => {
|
|
3454
|
+
const locals = topLocals();
|
|
3455
|
+
if (locals.node === node) {
|
|
3456
|
+
state.localsStack.pop();
|
|
3457
|
+
}
|
|
3458
|
+
const opt = optimizeNode(istate, node);
|
|
3459
|
+
if (opt != null) {
|
|
3460
|
+
return opt;
|
|
3461
|
+
}
|
|
3462
|
+
switch (node.type) {
|
|
3463
|
+
case "FunctionDeclaration":
|
|
3464
|
+
if (node.body && evaluateFunction(istate, node, null) !== false) {
|
|
3465
|
+
node.optimizable = true;
|
|
3466
|
+
}
|
|
3467
|
+
if (!state.currentFunction) {
|
|
3468
|
+
throw new Error(`Finished function ${state.top().sn.fullName}, but it was not marked current`);
|
|
3469
|
+
}
|
|
3470
|
+
state.currentFunction.info = state.currentFunction.next_info || false;
|
|
3471
|
+
delete state.currentFunction.next_info;
|
|
3472
|
+
delete state.currentFunction;
|
|
3473
|
+
if (istate.stack.length) {
|
|
3474
|
+
throw new Error("Stack was not empty");
|
|
3475
|
+
}
|
|
3476
|
+
istate = gistate;
|
|
3477
|
+
if (again) {
|
|
3478
|
+
again = false;
|
|
3479
|
+
const top = state.stack.pop();
|
|
3480
|
+
state.traverse(node);
|
|
3481
|
+
state.stack.push(top);
|
|
3482
|
+
}
|
|
3483
|
+
break;
|
|
3484
|
+
case "BlockStatement":
|
|
3485
|
+
case "ForStatement":
|
|
3486
|
+
if (locals.map && cleanupUnusedVars(state, node)) {
|
|
3487
|
+
again = true;
|
|
3488
|
+
}
|
|
3489
|
+
break;
|
|
3490
|
+
case "IfStatement": {
|
|
3491
|
+
const call = inlinableSubExpression(node.test);
|
|
3492
|
+
if (call) {
|
|
3493
|
+
return optimizeCallHelper(istate, call, node);
|
|
3494
|
+
}
|
|
3495
|
+
break;
|
|
3496
|
+
}
|
|
3497
|
+
case "ReturnStatement":
|
|
3498
|
+
if (node.argument && node.argument.type === "CallExpression") {
|
|
3499
|
+
return optimizeCallHelper(istate, node.argument, node);
|
|
3500
|
+
}
|
|
3501
|
+
break;
|
|
3502
|
+
case "Identifier":
|
|
3503
|
+
if (hasProperty(state.index, node.name)) {
|
|
3504
|
+
state.usedByName[node.name] = true;
|
|
3505
|
+
}
|
|
3506
|
+
break;
|
|
3507
|
+
case "MemberExpression": {
|
|
3508
|
+
const property = isLookupCandidate(node);
|
|
3509
|
+
if (property) {
|
|
3510
|
+
if (hasProperty(state.index, property.name)) {
|
|
3511
|
+
state.usedByName[property.name] = true;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
break;
|
|
3515
|
+
}
|
|
3516
|
+
case "NewExpression":
|
|
3517
|
+
if (state.currentFunction) {
|
|
3518
|
+
const [, results] = state.lookup(node.callee);
|
|
3519
|
+
if (results) {
|
|
3520
|
+
recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
|
|
3521
|
+
}
|
|
3522
|
+
else {
|
|
3523
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
break;
|
|
3527
|
+
case "CallExpression": {
|
|
3528
|
+
return optimizeCallHelper(istate, node, null);
|
|
3529
|
+
}
|
|
3530
|
+
case "VariableDeclaration": {
|
|
3531
|
+
const locals = topLocals();
|
|
3532
|
+
if (locals.map &&
|
|
3533
|
+
locals.node &&
|
|
3534
|
+
locals.node.type === "BlockStatement") {
|
|
3535
|
+
let results;
|
|
3536
|
+
const declarations = node.declarations;
|
|
3537
|
+
let i = 0;
|
|
3538
|
+
let j = 0;
|
|
3539
|
+
while (i < node.declarations.length) {
|
|
3540
|
+
const decl = declarations[i++];
|
|
3541
|
+
if (!decl.init)
|
|
3542
|
+
continue;
|
|
3543
|
+
const call = inlinableSubExpression(decl.init);
|
|
3544
|
+
if (call) {
|
|
3545
|
+
const inlined = optimizeCallHelper(istate, call, decl);
|
|
3546
|
+
if (!inlined)
|
|
3547
|
+
continue;
|
|
3548
|
+
if (Array.isArray(inlined) || inlined.type !== "BlockStatement") {
|
|
3549
|
+
throw new Error("Unexpected inlined result");
|
|
3550
|
+
}
|
|
3551
|
+
if (!results) {
|
|
3552
|
+
results = [];
|
|
3553
|
+
}
|
|
3554
|
+
delete decl.init;
|
|
3555
|
+
results.push(withLoc({
|
|
3556
|
+
...node,
|
|
3557
|
+
declarations: declarations.slice(j, i),
|
|
3558
|
+
}, j ? declarations[j] : null, decl.id));
|
|
3559
|
+
results.push(inlined);
|
|
3560
|
+
j = i;
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
if (results) {
|
|
3564
|
+
if (j < i) {
|
|
3565
|
+
results.push({
|
|
3566
|
+
...node,
|
|
3567
|
+
declarations: declarations.slice(j, i),
|
|
3568
|
+
});
|
|
3569
|
+
}
|
|
3570
|
+
return results;
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
break;
|
|
3574
|
+
}
|
|
3575
|
+
case "ExpressionStatement":
|
|
3576
|
+
if (node.expression.type === "CallExpression") {
|
|
3577
|
+
return optimizeCallHelper(istate, node.expression, node);
|
|
3578
|
+
}
|
|
3579
|
+
else if (node.expression.type === "AssignmentExpression") {
|
|
3580
|
+
const call = inlinableSubExpression(node.expression.right);
|
|
3581
|
+
if (call) {
|
|
3582
|
+
let ok = false;
|
|
3583
|
+
if (node.expression.left.type === "Identifier") {
|
|
3584
|
+
if (hasProperty(topLocals().map, node.expression.left.type)) {
|
|
3585
|
+
ok = true;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
if (!ok && node.expression.operator === "=") {
|
|
3589
|
+
const [, result] = state.lookup(node.expression.left);
|
|
3590
|
+
ok = !!result;
|
|
3591
|
+
}
|
|
3592
|
+
if (ok) {
|
|
3593
|
+
return optimizeCallHelper(istate, call, node.expression);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
else {
|
|
3598
|
+
return unused(state, node.expression, true);
|
|
3599
|
+
}
|
|
3600
|
+
break;
|
|
3601
|
+
case "AssignmentExpression":
|
|
3602
|
+
case "UpdateExpression":
|
|
3603
|
+
if (state.currentFunction) {
|
|
3604
|
+
const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
|
|
3605
|
+
const [, results] = state.lookup(lhs);
|
|
3606
|
+
if (results) {
|
|
3607
|
+
recordModifiedDecls(state.currentFunction, results);
|
|
3608
|
+
}
|
|
3609
|
+
else {
|
|
3610
|
+
const id = lhs.type === "Identifier" ? lhs : isLookupCandidate(lhs);
|
|
3611
|
+
if (id) {
|
|
3612
|
+
recordModifiedName(state.currentFunction, id.name);
|
|
3613
|
+
}
|
|
3614
|
+
else {
|
|
3615
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
break;
|
|
3620
|
+
}
|
|
3621
|
+
return null;
|
|
3622
|
+
};
|
|
3623
|
+
Object.values(fnMap).forEach((f) => {
|
|
3624
|
+
collectNamespaces(f.ast, state);
|
|
3625
|
+
});
|
|
3626
|
+
state.usedByName = {};
|
|
3627
|
+
state.calledFunctions = {};
|
|
3628
|
+
state.exposed = state.nextExposed;
|
|
3629
|
+
state.nextExposed = {};
|
|
3630
|
+
Object.values(fnMap).forEach((f) => {
|
|
3631
|
+
collectNamespaces(f.ast, state);
|
|
3632
|
+
});
|
|
3633
|
+
state.exposed = state.nextExposed;
|
|
3634
|
+
state.nextExposed = {};
|
|
3635
|
+
delete state.pre;
|
|
3636
|
+
delete state.post;
|
|
3637
|
+
Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
|
|
3638
|
+
const cleanup = (node) => {
|
|
3639
|
+
switch (node.type) {
|
|
3640
|
+
case "ThisExpression":
|
|
3641
|
+
node.text = "self";
|
|
3642
|
+
break;
|
|
3643
|
+
case "EnumStringBody":
|
|
3644
|
+
if (node.members.every((m) => {
|
|
3645
|
+
const name = "name" in m ? m.name : m.id.name;
|
|
3646
|
+
return (hasProperty(state.index, name) &&
|
|
3647
|
+
!hasProperty(state.exposed, name) &&
|
|
3648
|
+
!hasProperty(state.usedByName, name));
|
|
3649
|
+
})) {
|
|
3650
|
+
node.enumType = [
|
|
3651
|
+
...new Set(node.members.map((m) => {
|
|
3652
|
+
if (!("init" in m))
|
|
3653
|
+
return "Number";
|
|
3654
|
+
const [node, type] = getNodeValue(m.init);
|
|
3655
|
+
if (!node) {
|
|
3656
|
+
throw new Error("Failed to get type for eliminated enum");
|
|
3657
|
+
}
|
|
3658
|
+
return type;
|
|
3659
|
+
})),
|
|
3660
|
+
].join(" or ");
|
|
3661
|
+
node.members.splice(0);
|
|
3662
|
+
}
|
|
3663
|
+
break;
|
|
3664
|
+
case "EnumDeclaration":
|
|
3665
|
+
if (!node.body.members.length) {
|
|
3666
|
+
if (!node.id)
|
|
3667
|
+
return false;
|
|
3668
|
+
if (!node.body.enumType) {
|
|
3669
|
+
throw new Error("Missing enumType on optimized enum");
|
|
3670
|
+
}
|
|
3671
|
+
return {
|
|
3672
|
+
type: "TypedefDeclaration",
|
|
3673
|
+
id: node.id,
|
|
3674
|
+
ts: {
|
|
3675
|
+
type: "UnaryExpression",
|
|
3676
|
+
argument: {
|
|
3677
|
+
type: "TypeSpecList",
|
|
3678
|
+
ts: [
|
|
3679
|
+
node.body.enumType,
|
|
3680
|
+
],
|
|
3681
|
+
},
|
|
3682
|
+
prefix: true,
|
|
3683
|
+
operator: " as",
|
|
3684
|
+
},
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
break;
|
|
3688
|
+
case "VariableDeclaration": {
|
|
3689
|
+
node.declarations = node.declarations.filter((d) => {
|
|
3690
|
+
const name = variableDeclarationName(d.id);
|
|
3691
|
+
return (!hasProperty(state.index, name) ||
|
|
3692
|
+
hasProperty(state.exposed, name) ||
|
|
3693
|
+
hasProperty(state.usedByName, name));
|
|
3694
|
+
});
|
|
3695
|
+
if (!node.declarations.length) {
|
|
3696
|
+
return false;
|
|
3697
|
+
}
|
|
3698
|
+
break;
|
|
3699
|
+
}
|
|
3700
|
+
case "ClassElement":
|
|
3701
|
+
if (!node.item) {
|
|
3702
|
+
return false;
|
|
3703
|
+
}
|
|
3704
|
+
break;
|
|
3705
|
+
case "FunctionDeclaration":
|
|
3706
|
+
if (!maybeCalled(node)) {
|
|
3707
|
+
if (node.attrs &&
|
|
3708
|
+
node.attrs.attributes &&
|
|
3709
|
+
node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
|
|
3710
|
+
break;
|
|
3711
|
+
}
|
|
3712
|
+
return false;
|
|
3713
|
+
}
|
|
3714
|
+
break;
|
|
3715
|
+
case "ClassDeclaration":
|
|
3716
|
+
case "ModuleDeclaration":
|
|
3717
|
+
// none of the attributes means anything on classes and
|
|
3718
|
+
// modules, and the new compiler complains about some
|
|
3719
|
+
// of them. Just drop them all.
|
|
3720
|
+
if (node.attrs && node.attrs.access) {
|
|
3721
|
+
if (node.attrs.attributes) {
|
|
3722
|
+
delete node.attrs.access;
|
|
3723
|
+
}
|
|
3724
|
+
else {
|
|
3725
|
+
delete node.attrs;
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
return null;
|
|
3730
|
+
};
|
|
3731
|
+
Object.entries(fnMap).forEach(([, f]) => {
|
|
3732
|
+
traverseAst(f.ast, undefined, (node) => {
|
|
3733
|
+
const ret = cleanup(node);
|
|
3734
|
+
if (ret === false) {
|
|
3735
|
+
state.removeNodeComments(node, f.ast);
|
|
3736
|
+
}
|
|
3737
|
+
return ret;
|
|
3738
|
+
});
|
|
3739
|
+
});
|
|
3740
|
+
reportMissingSymbols(state, config);
|
|
3741
|
+
if (state.inlineDiagnostics) {
|
|
3742
|
+
if (!state.diagnostics) {
|
|
3743
|
+
state.diagnostics = state.inlineDiagnostics;
|
|
3744
|
+
}
|
|
3745
|
+
else {
|
|
3746
|
+
Object.entries(state.inlineDiagnostics).forEach(([key, diags]) => {
|
|
3747
|
+
if (!hasProperty(state.diagnostics, key)) {
|
|
3748
|
+
state.diagnostics[key] = diags;
|
|
3749
|
+
}
|
|
3750
|
+
else {
|
|
3751
|
+
state.diagnostics[key].push(...diags);
|
|
3752
|
+
}
|
|
3753
|
+
});
|
|
3754
|
+
}
|
|
3755
|
+
delete state.inlineDiagnostics;
|
|
3756
|
+
}
|
|
3757
|
+
Object.entries(fnMap).forEach(([name, f]) => {
|
|
3758
|
+
if (state.config && state.config.checkBuildPragmas) {
|
|
3759
|
+
pragmaChecker(state, f.ast, state.diagnostics?.[name]);
|
|
3760
|
+
}
|
|
3761
|
+
});
|
|
3762
|
+
return state.diagnostics;
|
|
3763
|
+
}
|
|
3764
|
+
function optimizeCall(istate, node, context) {
|
|
3765
|
+
const state = istate.state;
|
|
3766
|
+
const [name, results] = state.lookupNonlocal(node.callee);
|
|
3767
|
+
const callees = results ? findCallees(results) : null;
|
|
3768
|
+
if (!callees || !callees.length) {
|
|
3769
|
+
const n = name ||
|
|
3770
|
+
("name" in node.callee && node.callee.name) ||
|
|
3771
|
+
("property" in node.callee &&
|
|
3772
|
+
node.callee.property &&
|
|
3773
|
+
"name" in node.callee.property &&
|
|
3774
|
+
node.callee.property.name);
|
|
3775
|
+
if (n) {
|
|
3776
|
+
if (hasProperty(state.allFunctions, n)) {
|
|
3777
|
+
if (state.currentFunction) {
|
|
3778
|
+
recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
|
|
3779
|
+
}
|
|
3780
|
+
state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
else if (state.currentFunction) {
|
|
3784
|
+
// I don't think this can happen: foo[x](args)
|
|
3785
|
+
// doesn't parse, so you can't even do things like
|
|
3786
|
+
// $.Toybox.Lang[:format]("fmt", [])
|
|
3787
|
+
recordModifiedUnknown(state.currentFunction);
|
|
3788
|
+
}
|
|
3789
|
+
return null;
|
|
3790
|
+
}
|
|
3791
|
+
if (state.currentFunction) {
|
|
3792
|
+
recordCalledFuncs(state.currentFunction, callees);
|
|
3793
|
+
}
|
|
3794
|
+
if (callees.length === 1 && callees[0].type === "FunctionDeclaration") {
|
|
3795
|
+
const callee = callees[0].node;
|
|
3796
|
+
if (!context &&
|
|
3797
|
+
callee.optimizable &&
|
|
3798
|
+
!callee.hasOverride &&
|
|
3799
|
+
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
3800
|
+
const ret = evaluateFunction(istate, callee, node.arguments);
|
|
3801
|
+
if (ret) {
|
|
3802
|
+
return withLoc(ret, node, node);
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
if (shouldInline(state, callees[0], node, context)) {
|
|
3806
|
+
const ret = inlineFunction(state, callees[0], node, context);
|
|
3807
|
+
if (ret) {
|
|
3808
|
+
return ret;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
callees.forEach((c) => markFunctionCalled(state, c.node));
|
|
3813
|
+
return null;
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
|
|
2700
3817
|
/***/ }),
|
|
2701
3818
|
|
|
2702
3819
|
/***/ 4499:
|
|
@@ -2780,6 +3897,900 @@ var StateNodeAttributes;
|
|
|
2780
3897
|
})(StateNodeAttributes || (StateNodeAttributes = {}));
|
|
2781
3898
|
|
|
2782
3899
|
|
|
3900
|
+
/***/ }),
|
|
3901
|
+
|
|
3902
|
+
/***/ 1851:
|
|
3903
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3904
|
+
|
|
3905
|
+
"use strict";
|
|
3906
|
+
/* unused harmony export pragmaChecker */
|
|
3907
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
|
|
3908
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
3909
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
3910
|
+
|
|
3911
|
+
|
|
3912
|
+
function pragmaChecker(state, ast, diagnostics) {
|
|
3913
|
+
const comments = ast.comments;
|
|
3914
|
+
if (!comments)
|
|
3915
|
+
return;
|
|
3916
|
+
diagnostics = diagnostics
|
|
3917
|
+
?.slice()
|
|
3918
|
+
.sort((d1, d2) => d1.loc.start.offset - d2.loc.start.offset);
|
|
3919
|
+
let diagIndex = 0;
|
|
3920
|
+
let index = -1;
|
|
3921
|
+
let comment;
|
|
3922
|
+
let matchers;
|
|
3923
|
+
const next = () => {
|
|
3924
|
+
while (++index < comments.length) {
|
|
3925
|
+
comment = comments[index];
|
|
3926
|
+
let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
|
|
3927
|
+
if (!match)
|
|
3928
|
+
continue;
|
|
3929
|
+
const kind = match[1];
|
|
3930
|
+
let str = match[2];
|
|
3931
|
+
const verCheck = checkCompilerVersion(str.replace(/\s.*/, ""), state.sdkVersion || 0);
|
|
3932
|
+
if (verCheck === false)
|
|
3933
|
+
continue;
|
|
3934
|
+
if (verCheck === true) {
|
|
3935
|
+
str = str.replace(/^\S+\s+/, "");
|
|
3936
|
+
}
|
|
3937
|
+
matchers = [];
|
|
3938
|
+
while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
|
|
3939
|
+
matchers.push({ kind, quote: match[1], needle: match[2] });
|
|
3940
|
+
str = str.substring(match[0].length);
|
|
3941
|
+
if (!str.length)
|
|
3942
|
+
break;
|
|
3943
|
+
}
|
|
3944
|
+
if (!str.length)
|
|
3945
|
+
break;
|
|
3946
|
+
if (!matchers.length) {
|
|
3947
|
+
match = str.match(/^(\S+)\s+$/);
|
|
3948
|
+
if (match) {
|
|
3949
|
+
matchers.push({ kind, quote: '"', needle: match[1] });
|
|
3950
|
+
break;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
diagnostic(state, comment, `Build pragma '${comment.value}' is invalid`, "ERROR");
|
|
3954
|
+
}
|
|
3955
|
+
};
|
|
3956
|
+
const matcher = (quote, needle, haystack) => {
|
|
3957
|
+
if (quote === '"') {
|
|
3958
|
+
return haystack.includes(needle);
|
|
3959
|
+
}
|
|
3960
|
+
const re = new RegExp(needle.replace(/@([-\d.\w]+|"[^"]*")/g, (_match, pat) => `(?:${pat}|pre_${pat.replace(/\W/g, "_")}(?:_\\d+)?)`));
|
|
3961
|
+
return re.test(haystack);
|
|
3962
|
+
};
|
|
3963
|
+
next();
|
|
3964
|
+
traverseAst(ast, (node) => {
|
|
3965
|
+
if (index >= comments.length ||
|
|
3966
|
+
node.type === "Line" ||
|
|
3967
|
+
node.type === "Block" ||
|
|
3968
|
+
node.type === "MultiLine") {
|
|
3969
|
+
return false;
|
|
3970
|
+
}
|
|
3971
|
+
if (node.start && node.start >= (comment.end || Infinity)) {
|
|
3972
|
+
const { kind, quote, needle } = matchers.shift();
|
|
3973
|
+
if (kind === "match") {
|
|
3974
|
+
const haystack = formatAst(node).replace(/([\r\n]|\s)+/g, " ");
|
|
3975
|
+
if (!matcher(quote, needle, haystack)) {
|
|
3976
|
+
matcher(quote, needle, haystack);
|
|
3977
|
+
diagnostic(state, comment, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
else if (kind === "expect") {
|
|
3981
|
+
const locCmp = (a, b) => {
|
|
3982
|
+
if (!b)
|
|
3983
|
+
return -1;
|
|
3984
|
+
if (a.start.line < b.start.line)
|
|
3985
|
+
return -1;
|
|
3986
|
+
if (a.start.line === b.start.line &&
|
|
3987
|
+
a.start.column < b.start.column) {
|
|
3988
|
+
return -1;
|
|
3989
|
+
}
|
|
3990
|
+
if (a.end.line > b.end.line)
|
|
3991
|
+
return 1;
|
|
3992
|
+
if (a.end.line === b.end.line && a.end.column >= b.end.column) {
|
|
3993
|
+
return 1;
|
|
3994
|
+
}
|
|
3995
|
+
return 0;
|
|
3996
|
+
};
|
|
3997
|
+
let found = false;
|
|
3998
|
+
if (diagnostics) {
|
|
3999
|
+
while (true) {
|
|
4000
|
+
if (diagIndex >= diagnostics.length) {
|
|
4001
|
+
diagnostics = null;
|
|
4002
|
+
break;
|
|
4003
|
+
}
|
|
4004
|
+
const diag = diagnostics[diagIndex];
|
|
4005
|
+
const cmp = locCmp(diag.loc, node.loc);
|
|
4006
|
+
if (cmp > 0) {
|
|
4007
|
+
break;
|
|
4008
|
+
}
|
|
4009
|
+
diagIndex++;
|
|
4010
|
+
if (cmp < 0)
|
|
4011
|
+
continue;
|
|
4012
|
+
if (matcher(quote, needle, diag.message)) {
|
|
4013
|
+
found = true;
|
|
4014
|
+
diag.type = "INFO";
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
if (!found) {
|
|
4019
|
+
diagnostic(state, comment, `Missing error message '${needle}`, "ERROR");
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
if (matchers.length) {
|
|
4023
|
+
// if we're checking a series of nodes, we need
|
|
4024
|
+
// to skip over this one.
|
|
4025
|
+
return false;
|
|
4026
|
+
}
|
|
4027
|
+
next();
|
|
4028
|
+
}
|
|
4029
|
+
return null;
|
|
4030
|
+
});
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
|
|
4034
|
+
/***/ }),
|
|
4035
|
+
|
|
4036
|
+
/***/ 9910:
|
|
4037
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
4038
|
+
|
|
4039
|
+
"use strict";
|
|
4040
|
+
/* unused harmony export sizeBasedPRE */
|
|
4041
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
|
|
4042
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
4043
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
4044
|
+
/* harmony import */ var _control_flow__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5101);
|
|
4045
|
+
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8180);
|
|
4046
|
+
/* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(819);
|
|
4047
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6906);
|
|
4048
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_5__);
|
|
4049
|
+
|
|
4050
|
+
|
|
4051
|
+
|
|
4052
|
+
|
|
4053
|
+
|
|
4054
|
+
|
|
4055
|
+
/**
|
|
4056
|
+
* This implements a pseudo Partial Redundancy Elimination
|
|
4057
|
+
* pass. It isn't quite like traditional PRE because we're
|
|
4058
|
+
* aiming to minimize size, not dynamic instructions. So
|
|
4059
|
+
* for us, its worthwhile to take something like:
|
|
4060
|
+
*
|
|
4061
|
+
* switch (x) {
|
|
4062
|
+
* case 1: foo(A.B); break;
|
|
4063
|
+
* case 2: foo(C); break;
|
|
4064
|
+
* case 3: bar(A.B); break;
|
|
4065
|
+
* }
|
|
4066
|
+
*
|
|
4067
|
+
* and rewrite it as
|
|
4068
|
+
*
|
|
4069
|
+
* var tmp = A.B;
|
|
4070
|
+
* switch (x) {
|
|
4071
|
+
* case 1: foo(tmp); break;
|
|
4072
|
+
* case 2: foo(C); break;
|
|
4073
|
+
* case 3: bar(tmp); break;
|
|
4074
|
+
* }
|
|
4075
|
+
*
|
|
4076
|
+
* because even though A.B wasn't used on all paths where we
|
|
4077
|
+
* inserted the temporary, we still reduced the code size.
|
|
4078
|
+
*/
|
|
4079
|
+
const logging = false;
|
|
4080
|
+
function logAntState(s, decl) {
|
|
4081
|
+
const defs = Array.from(s.ant).reduce((defs, event) => {
|
|
4082
|
+
if (event.type === "def" || event.type === "mod")
|
|
4083
|
+
defs++;
|
|
4084
|
+
return defs;
|
|
4085
|
+
}, 0);
|
|
4086
|
+
console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
|
|
4087
|
+
console.log(` - members: ${Array.from(s.members)
|
|
4088
|
+
.map(([block, live]) => block.order + (live ? "t" : "f"))
|
|
4089
|
+
.join(", ")}`);
|
|
4090
|
+
}
|
|
4091
|
+
function logAntDecls(antDecls) {
|
|
4092
|
+
antDecls.forEach(logAntState);
|
|
4093
|
+
}
|
|
4094
|
+
function sizeBasedPRE(state, func) {
|
|
4095
|
+
if (!func.node.body)
|
|
4096
|
+
return;
|
|
4097
|
+
if (!state.config ||
|
|
4098
|
+
!state.config.sizeBasedPRE ||
|
|
4099
|
+
(typeof state.config.sizeBasedPRE === "string" &&
|
|
4100
|
+
state.config.sizeBasedPRE !== func.fullName)) {
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
const { graph: head, identifiers } = buildPREGraph(state, func);
|
|
4104
|
+
const candidates = computeAttributes(state, head);
|
|
4105
|
+
if (candidates) {
|
|
4106
|
+
if (logging) {
|
|
4107
|
+
console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
|
|
4108
|
+
logAntDecls(candidates);
|
|
4109
|
+
}
|
|
4110
|
+
const nodeMap = new Map();
|
|
4111
|
+
const declMap = new Map();
|
|
4112
|
+
const variableDecl = withLoc({
|
|
4113
|
+
type: "VariableDeclaration",
|
|
4114
|
+
declarations: [],
|
|
4115
|
+
kind: "var",
|
|
4116
|
+
}, func.node.body);
|
|
4117
|
+
variableDecl.end = variableDecl.start;
|
|
4118
|
+
variableDecl.loc.end = variableDecl.loc.start;
|
|
4119
|
+
Array.from(candidates.keys())
|
|
4120
|
+
.map((decl) => [declFullName(decl), decl])
|
|
4121
|
+
.sort()
|
|
4122
|
+
.forEach(([, decl]) => {
|
|
4123
|
+
let name;
|
|
4124
|
+
let i = 0;
|
|
4125
|
+
do {
|
|
4126
|
+
name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
|
|
4127
|
+
if (!identifiers.has(name)) {
|
|
4128
|
+
identifiers.add(name);
|
|
4129
|
+
break;
|
|
4130
|
+
}
|
|
4131
|
+
i++;
|
|
4132
|
+
} while (true);
|
|
4133
|
+
declMap.set(decl, name);
|
|
4134
|
+
const s = candidates.get(decl);
|
|
4135
|
+
variableDecl.declarations.push(withLoc({
|
|
4136
|
+
type: "VariableDeclarator",
|
|
4137
|
+
id: withLoc({ type: "Identifier", name }, variableDecl),
|
|
4138
|
+
kind: "var",
|
|
4139
|
+
}, variableDecl));
|
|
4140
|
+
s.ant.forEach((event) => {
|
|
4141
|
+
const events = nodeMap.get(event.node);
|
|
4142
|
+
if (!events) {
|
|
4143
|
+
nodeMap.set(event.node, [event]);
|
|
4144
|
+
}
|
|
4145
|
+
else {
|
|
4146
|
+
events.push(event);
|
|
4147
|
+
}
|
|
4148
|
+
});
|
|
4149
|
+
});
|
|
4150
|
+
applyReplacements(func.node, nodeMap, declMap);
|
|
4151
|
+
func.node.body.body.unshift(variableDecl);
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
function buildPREGraph(state, func) {
|
|
4155
|
+
const result = buildDataFlowGraph(state, func, (literal) => refCost(literal) > LocalRefCost, true, false);
|
|
4156
|
+
// Split blocks that contains refs and defs to the same thing,
|
|
4157
|
+
// to make it easier to pick and choose which refs are converted
|
|
4158
|
+
postOrderTraverse(result.graph, (block) => {
|
|
4159
|
+
if (!block.events) {
|
|
4160
|
+
return;
|
|
4161
|
+
}
|
|
4162
|
+
let modSeen = false;
|
|
4163
|
+
const refs = new Set();
|
|
4164
|
+
const defs = new Set();
|
|
4165
|
+
const splitBlock = (eventIndex) => {
|
|
4166
|
+
const newBlock = { ...block };
|
|
4167
|
+
delete newBlock.node;
|
|
4168
|
+
newBlock.events = block.events?.splice(eventIndex);
|
|
4169
|
+
newBlock.succs?.forEach((succ) => {
|
|
4170
|
+
const i = succ.preds.findIndex((b) => b === block);
|
|
4171
|
+
succ.preds[i] = newBlock;
|
|
4172
|
+
if (succ.expreds?.includes(block)) {
|
|
4173
|
+
succ.expreds.push(newBlock);
|
|
4174
|
+
}
|
|
4175
|
+
});
|
|
4176
|
+
block.succs = [newBlock];
|
|
4177
|
+
newBlock.preds = [block];
|
|
4178
|
+
delete newBlock.expreds;
|
|
4179
|
+
modSeen = false;
|
|
4180
|
+
refs.clear();
|
|
4181
|
+
defs.clear();
|
|
4182
|
+
};
|
|
4183
|
+
for (let i = block.events.length; i--;) {
|
|
4184
|
+
const event = block.events[i];
|
|
4185
|
+
switch (event.type) {
|
|
4186
|
+
case "ref":
|
|
4187
|
+
if (some(event.decl, (decl) => decl.type === "Literal" ||
|
|
4188
|
+
(decl.type === "VariableDeclarator" &&
|
|
4189
|
+
decl.node.kind === "const"))) {
|
|
4190
|
+
break;
|
|
4191
|
+
}
|
|
4192
|
+
if (modSeen || defs.has(event.decl)) {
|
|
4193
|
+
splitBlock(i + 1);
|
|
4194
|
+
}
|
|
4195
|
+
refs.add(event.decl);
|
|
4196
|
+
break;
|
|
4197
|
+
case "def":
|
|
4198
|
+
if (refs.has(event.decl)) {
|
|
4199
|
+
splitBlock(i + 1);
|
|
4200
|
+
}
|
|
4201
|
+
defs.add(event.decl);
|
|
4202
|
+
break;
|
|
4203
|
+
case "mod":
|
|
4204
|
+
if (event.callees &&
|
|
4205
|
+
every(event.callees, (callee) => callee.info === false)) {
|
|
4206
|
+
// calls known to have no side effects can be
|
|
4207
|
+
// dropped.
|
|
4208
|
+
block.events.splice(i, 1);
|
|
4209
|
+
break;
|
|
4210
|
+
}
|
|
4211
|
+
if (refs.size) {
|
|
4212
|
+
splitBlock(i + 1);
|
|
4213
|
+
}
|
|
4214
|
+
modSeen = true;
|
|
4215
|
+
break;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
});
|
|
4219
|
+
return result;
|
|
4220
|
+
}
|
|
4221
|
+
function anticipatedDecls() {
|
|
4222
|
+
return new Map();
|
|
4223
|
+
}
|
|
4224
|
+
function equalSet(a, b) {
|
|
4225
|
+
if (a.size !== b.size)
|
|
4226
|
+
return false;
|
|
4227
|
+
for (const item of a) {
|
|
4228
|
+
if (!b.has(item))
|
|
4229
|
+
return false;
|
|
4230
|
+
}
|
|
4231
|
+
return true;
|
|
4232
|
+
}
|
|
4233
|
+
function equalMap(a, b) {
|
|
4234
|
+
if (a.size !== b.size)
|
|
4235
|
+
return false;
|
|
4236
|
+
for (const [item, value] of a) {
|
|
4237
|
+
if (b.get(item) !== value)
|
|
4238
|
+
return false;
|
|
4239
|
+
}
|
|
4240
|
+
return true;
|
|
4241
|
+
}
|
|
4242
|
+
function anticipatedState(node, events) {
|
|
4243
|
+
return { ant: events || new Set(), live: true, node, members: new Map() };
|
|
4244
|
+
}
|
|
4245
|
+
function cloneAnticipatedState(as) {
|
|
4246
|
+
return {
|
|
4247
|
+
ant: cloneSet(as.ant),
|
|
4248
|
+
live: as.live,
|
|
4249
|
+
node: as.node,
|
|
4250
|
+
members: new Map(as.members),
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
function mergeAnticipatedState(ae, be) {
|
|
4254
|
+
mergeSet(ae.ant, be.ant);
|
|
4255
|
+
be.members.forEach((live, block) => ae.members.set(block, live));
|
|
4256
|
+
if (be.live)
|
|
4257
|
+
ae.live = true;
|
|
4258
|
+
}
|
|
4259
|
+
function cloneAnticipatedDecls(ad) {
|
|
4260
|
+
const copy = anticipatedDecls();
|
|
4261
|
+
for (const [k, v] of ad) {
|
|
4262
|
+
if (!v.isIsolated) {
|
|
4263
|
+
copy.set(k, cloneAnticipatedState(v));
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
return copy;
|
|
4267
|
+
}
|
|
4268
|
+
function mergeAnticipatedDecls(a, b) {
|
|
4269
|
+
for (const [k, v] of b) {
|
|
4270
|
+
if (v.isIsolated)
|
|
4271
|
+
continue;
|
|
4272
|
+
const ae = a.get(k);
|
|
4273
|
+
if (ae) {
|
|
4274
|
+
mergeAnticipatedState(ae, v);
|
|
4275
|
+
}
|
|
4276
|
+
else {
|
|
4277
|
+
a.set(k, cloneAnticipatedState(v));
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
function equalStates(a, b) {
|
|
4282
|
+
if (a.size !== b.size)
|
|
4283
|
+
return false;
|
|
4284
|
+
for (const [k, ae] of a) {
|
|
4285
|
+
const be = b.get(k);
|
|
4286
|
+
if (!be ||
|
|
4287
|
+
be.live !== ae.live ||
|
|
4288
|
+
be.isIsolated !== ae.isIsolated ||
|
|
4289
|
+
!equalSet(ae.ant, be.ant) ||
|
|
4290
|
+
!equalMap(ae.members, be.members)) {
|
|
4291
|
+
return false;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
return true;
|
|
4295
|
+
}
|
|
4296
|
+
const LocalRefCost = 2;
|
|
4297
|
+
function refCost(node) {
|
|
4298
|
+
if (node.type === "Literal") {
|
|
4299
|
+
switch (typeof node.value) {
|
|
4300
|
+
case "string":
|
|
4301
|
+
return 5;
|
|
4302
|
+
case "bigint":
|
|
4303
|
+
case "number":
|
|
4304
|
+
return 5;
|
|
4305
|
+
case "boolean":
|
|
4306
|
+
return 2;
|
|
4307
|
+
default:
|
|
4308
|
+
if (node.value === null) {
|
|
4309
|
+
return 2;
|
|
4310
|
+
}
|
|
4311
|
+
return 0;
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
// A read from a non-local identifier takes 8 bytes
|
|
4315
|
+
let cost = 8;
|
|
4316
|
+
if (node.type === "Identifier")
|
|
4317
|
+
return cost;
|
|
4318
|
+
while (true) {
|
|
4319
|
+
const next = node.object;
|
|
4320
|
+
if (next.type !== "MemberExpression") {
|
|
4321
|
+
if (next.type !== "ThisExpression") {
|
|
4322
|
+
cost += next.type === "Identifier" && next.name === "$" ? 4 : 6;
|
|
4323
|
+
}
|
|
4324
|
+
return cost;
|
|
4325
|
+
}
|
|
4326
|
+
node = next;
|
|
4327
|
+
cost += 6;
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
function defCost(node) {
|
|
4331
|
+
return refCost(node) + 2;
|
|
4332
|
+
}
|
|
4333
|
+
function candidateBoundary(candState) {
|
|
4334
|
+
const boundary = new Set();
|
|
4335
|
+
candState.members.forEach((live, block) => {
|
|
4336
|
+
if (live && block !== candState.head) {
|
|
4337
|
+
if (block.preds) {
|
|
4338
|
+
block.preds.forEach((pred) => candState.members.has(pred) || boundary.add(pred));
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
});
|
|
4342
|
+
if (candState.live) {
|
|
4343
|
+
if (!candState.head) {
|
|
4344
|
+
throw new Error(`Missing head`);
|
|
4345
|
+
}
|
|
4346
|
+
boundary.add(candState.head);
|
|
4347
|
+
}
|
|
4348
|
+
return boundary;
|
|
4349
|
+
}
|
|
4350
|
+
function candidateCost(candState) {
|
|
4351
|
+
let cost = 0;
|
|
4352
|
+
candState.ant.forEach((event) => {
|
|
4353
|
+
if (event.type === "ref") {
|
|
4354
|
+
cost -= refCost(candState.node) - LocalRefCost;
|
|
4355
|
+
}
|
|
4356
|
+
else {
|
|
4357
|
+
cost += defCost(candState.node);
|
|
4358
|
+
}
|
|
4359
|
+
});
|
|
4360
|
+
const boundarySize = candidateBoundary(candState).size;
|
|
4361
|
+
cost += defCost(candState.node) * boundarySize;
|
|
4362
|
+
return cost;
|
|
4363
|
+
}
|
|
4364
|
+
function computeAttributes(state, head) {
|
|
4365
|
+
const order = getPostOrder(head);
|
|
4366
|
+
order.forEach((block, i) => {
|
|
4367
|
+
block.order = i;
|
|
4368
|
+
});
|
|
4369
|
+
if (logging) {
|
|
4370
|
+
order.forEach((block) => {
|
|
4371
|
+
console.log(block.order, `(${block.node ? block.node.loc?.start.line : "??"})`, `Preds: ${(block.preds || [])
|
|
4372
|
+
.map((block) => block.order)
|
|
4373
|
+
.join(", ")}`);
|
|
4374
|
+
if (block.events) {
|
|
4375
|
+
block.events.forEach((event) => event.type !== "exn" &&
|
|
4376
|
+
console.log(` ${event.type}: ${event.decl
|
|
4377
|
+
? declFullName(event.decl)
|
|
4378
|
+
: event.node
|
|
4379
|
+
? formatAst(event.node)
|
|
4380
|
+
: "??"}`));
|
|
4381
|
+
}
|
|
4382
|
+
console.log(`Succs: ${(block.succs || [])
|
|
4383
|
+
.map((block) => block.order)
|
|
4384
|
+
.join(", ")} ExSucc: ${block.exsucc ? block.exsucc.order : ""}`);
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
const queue = new DataflowQueue();
|
|
4388
|
+
const blockStates = [];
|
|
4389
|
+
/*
|
|
4390
|
+
Algorithm
|
|
4391
|
+
=========
|
|
4392
|
+
|
|
4393
|
+
Process blocks in post-order, and the events in reverse
|
|
4394
|
+
order to collect the AnticipatedState at the start of each
|
|
4395
|
+
Block.
|
|
4396
|
+
|
|
4397
|
+
Then for each EventDecl find the best starting block.
|
|
4398
|
+
*/
|
|
4399
|
+
const modMap = new Map();
|
|
4400
|
+
const getMod = (event, decl, id) => {
|
|
4401
|
+
if (id.type !== "Identifier" && id.type !== "MemberExpression") {
|
|
4402
|
+
throw new Error("Trying to modify a non-variable");
|
|
4403
|
+
}
|
|
4404
|
+
let eventMap = modMap.get(event);
|
|
4405
|
+
if (!eventMap) {
|
|
4406
|
+
modMap.set(event, (eventMap = new Map()));
|
|
4407
|
+
}
|
|
4408
|
+
let result = eventMap.get(decl);
|
|
4409
|
+
if (!result) {
|
|
4410
|
+
result = {
|
|
4411
|
+
type: "mod",
|
|
4412
|
+
node: event.node,
|
|
4413
|
+
decl,
|
|
4414
|
+
id,
|
|
4415
|
+
mayThrow: event.mayThrow,
|
|
4416
|
+
};
|
|
4417
|
+
eventMap.set(decl, result);
|
|
4418
|
+
}
|
|
4419
|
+
return result;
|
|
4420
|
+
};
|
|
4421
|
+
order.forEach((block) => queue.enqueue(block));
|
|
4422
|
+
while (!queue.empty()) {
|
|
4423
|
+
const top = queue.dequeue();
|
|
4424
|
+
if (top.order === undefined) {
|
|
4425
|
+
throw new Error(`Unreachable block was visited!`);
|
|
4426
|
+
}
|
|
4427
|
+
const curState = (top.succs &&
|
|
4428
|
+
top.succs.reduce((blockState, succ) => {
|
|
4429
|
+
const succState = blockStates[succ.order];
|
|
4430
|
+
if (succState) {
|
|
4431
|
+
if (!blockState) {
|
|
4432
|
+
blockState = cloneAnticipatedDecls(succState);
|
|
4433
|
+
}
|
|
4434
|
+
else {
|
|
4435
|
+
mergeAnticipatedDecls(blockState, succState);
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
return blockState;
|
|
4439
|
+
}, null)) ||
|
|
4440
|
+
anticipatedDecls();
|
|
4441
|
+
if (top.events) {
|
|
4442
|
+
for (let i = top.events.length; i--;) {
|
|
4443
|
+
const event = top.events[i];
|
|
4444
|
+
if (event.mayThrow && top.exsucc) {
|
|
4445
|
+
const succState = blockStates[top.exsucc.order];
|
|
4446
|
+
if (succState) {
|
|
4447
|
+
mergeAnticipatedDecls(curState, succState);
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
switch (event.type) {
|
|
4451
|
+
case "ref": {
|
|
4452
|
+
let candidates = curState.get(event.decl);
|
|
4453
|
+
if (!candidates) {
|
|
4454
|
+
candidates = anticipatedState(event.node);
|
|
4455
|
+
curState.set(event.decl, candidates);
|
|
4456
|
+
}
|
|
4457
|
+
candidates.ant.add(event);
|
|
4458
|
+
candidates.live = true;
|
|
4459
|
+
break;
|
|
4460
|
+
}
|
|
4461
|
+
case "mod": {
|
|
4462
|
+
curState.forEach((candidates, decls) => {
|
|
4463
|
+
if (some(decls, (decl) => decl.type === "VariableDeclarator" &&
|
|
4464
|
+
decl.node.kind === "var" &&
|
|
4465
|
+
candidates.live &&
|
|
4466
|
+
(!event.callees ||
|
|
4467
|
+
event.callees.some((callee) => functionMayModify(state, callee, decl))))) {
|
|
4468
|
+
candidates.ant.add(getMod(event, decls, candidates.node));
|
|
4469
|
+
candidates.live = false;
|
|
4470
|
+
}
|
|
4471
|
+
});
|
|
4472
|
+
break;
|
|
4473
|
+
}
|
|
4474
|
+
case "def": {
|
|
4475
|
+
let candidates = curState.get(event.decl);
|
|
4476
|
+
const isUpdate = event.node.type === "UpdateExpression" ||
|
|
4477
|
+
(event.node.type === "AssignmentExpression" &&
|
|
4478
|
+
event.node.operator !== "=");
|
|
4479
|
+
if (!candidates) {
|
|
4480
|
+
const target = event.node.type === "AssignmentExpression"
|
|
4481
|
+
? event.node.left
|
|
4482
|
+
: event.node.type === "UpdateExpression"
|
|
4483
|
+
? event.node.argument
|
|
4484
|
+
: event.node.id.type === "BinaryExpression"
|
|
4485
|
+
? event.node.id.left
|
|
4486
|
+
: event.node.id;
|
|
4487
|
+
candidates = anticipatedState(target);
|
|
4488
|
+
curState.set(event.decl, candidates);
|
|
4489
|
+
}
|
|
4490
|
+
if (isUpdate || candidates.live) {
|
|
4491
|
+
candidates.ant.add(event);
|
|
4492
|
+
}
|
|
4493
|
+
candidates.live = isUpdate;
|
|
4494
|
+
break;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
curState.forEach((antState) => {
|
|
4500
|
+
antState.head = top;
|
|
4501
|
+
antState.members.set(top, antState.live);
|
|
4502
|
+
if (!antState.live && candidateBoundary(antState).size === 0) {
|
|
4503
|
+
// we found a group that's isolated from the rest
|
|
4504
|
+
// of the function. Don't merge it with earlier
|
|
4505
|
+
// refs and defs, because we can take it or leave
|
|
4506
|
+
// it based on its own cost.
|
|
4507
|
+
antState.isIsolated = true;
|
|
4508
|
+
}
|
|
4509
|
+
});
|
|
4510
|
+
const oldState = blockStates[top.order];
|
|
4511
|
+
if (oldState && equalStates(oldState, curState)) {
|
|
4512
|
+
continue;
|
|
4513
|
+
}
|
|
4514
|
+
blockStates[top.order] = curState;
|
|
4515
|
+
if (logging) {
|
|
4516
|
+
console.log(`Updated block ${top.order}`);
|
|
4517
|
+
logAntDecls(curState);
|
|
4518
|
+
}
|
|
4519
|
+
if (top.preds) {
|
|
4520
|
+
top.preds.forEach((pred) => queue.enqueue(pred));
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
const candidateDecls = anticipatedDecls();
|
|
4524
|
+
blockStates.forEach((blockState, i) => {
|
|
4525
|
+
blockState &&
|
|
4526
|
+
blockState.forEach((events, decl) => {
|
|
4527
|
+
const cost = candidateCost(events);
|
|
4528
|
+
if (cost >= 0)
|
|
4529
|
+
return;
|
|
4530
|
+
const existing = candidateDecls.get(decl);
|
|
4531
|
+
if (!existing ||
|
|
4532
|
+
existing.isIsolated ||
|
|
4533
|
+
candidateCost(existing) > cost) {
|
|
4534
|
+
const boundary = candidateBoundary(events);
|
|
4535
|
+
if (!Array.from(boundary).every((block) => {
|
|
4536
|
+
if (block !== events.head && block.events) {
|
|
4537
|
+
if (events.node.type === "Literal") {
|
|
4538
|
+
return false;
|
|
4539
|
+
}
|
|
4540
|
+
let i = block.events.length;
|
|
4541
|
+
while (i--) {
|
|
4542
|
+
const event = block.events[i];
|
|
4543
|
+
if (event.type === "def" || event.type === "mod") {
|
|
4544
|
+
events.ant.add({
|
|
4545
|
+
type: "mod",
|
|
4546
|
+
node: event.node,
|
|
4547
|
+
decl,
|
|
4548
|
+
id: events.node,
|
|
4549
|
+
mayThrow: false,
|
|
4550
|
+
});
|
|
4551
|
+
events.members.set(block, false);
|
|
4552
|
+
return true;
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
const node = block.node;
|
|
4557
|
+
if (!node)
|
|
4558
|
+
return false;
|
|
4559
|
+
events.ant.add({
|
|
4560
|
+
type: "mod",
|
|
4561
|
+
node: node.type === "FunctionDeclaration" ? node.body : node,
|
|
4562
|
+
before: true,
|
|
4563
|
+
decl,
|
|
4564
|
+
id: events.node,
|
|
4565
|
+
mayThrow: false,
|
|
4566
|
+
});
|
|
4567
|
+
events.members.set(block, false);
|
|
4568
|
+
return true;
|
|
4569
|
+
})) {
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
events.live = false;
|
|
4573
|
+
if (existing && existing.isIsolated) {
|
|
4574
|
+
delete existing.isIsolated;
|
|
4575
|
+
mergeAnticipatedState(events, existing);
|
|
4576
|
+
}
|
|
4577
|
+
else if (candidateCost(events) !== cost) {
|
|
4578
|
+
throw new Error(`cost of block ${i} changed`);
|
|
4579
|
+
}
|
|
4580
|
+
candidateDecls.set(decl, events);
|
|
4581
|
+
}
|
|
4582
|
+
});
|
|
4583
|
+
});
|
|
4584
|
+
if (candidateDecls.size) {
|
|
4585
|
+
return candidateDecls;
|
|
4586
|
+
}
|
|
4587
|
+
return null;
|
|
4588
|
+
}
|
|
4589
|
+
/*
|
|
4590
|
+
* Determine the cost of fixing a def under a statement.
|
|
4591
|
+
*
|
|
4592
|
+
* eg:
|
|
4593
|
+
*
|
|
4594
|
+
* if (foo()) {
|
|
4595
|
+
* bar(X.y);
|
|
4596
|
+
* } else {
|
|
4597
|
+
* baz(X.y);
|
|
4598
|
+
* }
|
|
4599
|
+
*
|
|
4600
|
+
* Here, we could pull out X.y as a local, but if foo might modify
|
|
4601
|
+
* X.y, we have nowhere to insert the temporary. But we can rewrite
|
|
4602
|
+
* it as:
|
|
4603
|
+
*
|
|
4604
|
+
* var tmp = foo();
|
|
4605
|
+
* if (tmp) {
|
|
4606
|
+
* bar(X.y);
|
|
4607
|
+
* } else {
|
|
4608
|
+
* baz(X.y);
|
|
4609
|
+
* }
|
|
4610
|
+
*
|
|
4611
|
+
* and now we can insert a temporary before the if, but it costs
|
|
4612
|
+
* 4 bytes to do so.
|
|
4613
|
+
*
|
|
4614
|
+
* We can do the same for switch statements unless (ugh!)
|
|
4615
|
+
* the cases might modify the decl too.
|
|
4616
|
+
*
|
|
4617
|
+
* eg
|
|
4618
|
+
*
|
|
4619
|
+
* switch (foo()) {
|
|
4620
|
+
* case bar(): ...
|
|
4621
|
+
* }
|
|
4622
|
+
*
|
|
4623
|
+
*/
|
|
4624
|
+
function _isFixableStmt(node) {
|
|
4625
|
+
switch (node.type) {
|
|
4626
|
+
case "IfStatement":
|
|
4627
|
+
return 4;
|
|
4628
|
+
case "SwitchStatement":
|
|
4629
|
+
if (node.cases.every((c) => !c.test ||
|
|
4630
|
+
c.test.type === "Literal" ||
|
|
4631
|
+
c.test.type === "Identifier" ||
|
|
4632
|
+
c.test.type === "InstanceOfCase" ||
|
|
4633
|
+
(c.test.type === "UnaryExpression" && c.test.operator === ":"))) {
|
|
4634
|
+
return 4;
|
|
4635
|
+
}
|
|
4636
|
+
break;
|
|
4637
|
+
}
|
|
4638
|
+
return false;
|
|
4639
|
+
}
|
|
4640
|
+
function applyReplacements(func, nodeMap, declMap) {
|
|
4641
|
+
const ident = (name, node) => {
|
|
4642
|
+
return withLoc({ type: "Identifier", name }, node);
|
|
4643
|
+
};
|
|
4644
|
+
const pendingMap = new Map();
|
|
4645
|
+
const stmtStack = [func];
|
|
4646
|
+
traverseAst(func, (node) => {
|
|
4647
|
+
if (isStatement(node)) {
|
|
4648
|
+
stmtStack.push(node);
|
|
4649
|
+
}
|
|
4650
|
+
}, (node) => {
|
|
4651
|
+
const stmt = stmtStack[stmtStack.length - 1];
|
|
4652
|
+
if (stmt === node)
|
|
4653
|
+
stmtStack.pop();
|
|
4654
|
+
const events = nodeMap.get(node);
|
|
4655
|
+
if (events) {
|
|
4656
|
+
const ret = events.reduce((ret, event) => {
|
|
4657
|
+
if (event.type === "ref") {
|
|
4658
|
+
if (ret) {
|
|
4659
|
+
throw new Error(`ref found when there was already a replacement for this node`);
|
|
4660
|
+
}
|
|
4661
|
+
if (node.type !== "Identifier" &&
|
|
4662
|
+
node.type !== "MemberExpression" &&
|
|
4663
|
+
node.type !== "Literal") {
|
|
4664
|
+
throw new Error(`Ref found, but wrong type of node: ${node.type}`);
|
|
4665
|
+
}
|
|
4666
|
+
const name = declMap.get(event.decl);
|
|
4667
|
+
if (!name) {
|
|
4668
|
+
throw new Error(`No replacement found for "${formatAst(node)}"`);
|
|
4669
|
+
}
|
|
4670
|
+
return ident(name, node);
|
|
4671
|
+
}
|
|
4672
|
+
if (event.type === "def") {
|
|
4673
|
+
if (ret) {
|
|
4674
|
+
throw new Error(`def found when there was already a replacement for this node`);
|
|
4675
|
+
}
|
|
4676
|
+
if (node.type !== "AssignmentExpression" &&
|
|
4677
|
+
node.type !== "UpdateExpression") {
|
|
4678
|
+
throw new Error(`Def found, but wrong type of node: ${node.type}`);
|
|
4679
|
+
}
|
|
4680
|
+
const target = node.type === "AssignmentExpression"
|
|
4681
|
+
? node.left
|
|
4682
|
+
: node.argument;
|
|
4683
|
+
const name = declMap.get(event.decl);
|
|
4684
|
+
if (!name) {
|
|
4685
|
+
throw new Error(`No replacement found for "${formatAst(target)}"`);
|
|
4686
|
+
}
|
|
4687
|
+
const id = ident(name, target);
|
|
4688
|
+
const assign = withLoc({
|
|
4689
|
+
type: "AssignmentExpression",
|
|
4690
|
+
left: target,
|
|
4691
|
+
right: { ...id },
|
|
4692
|
+
operator: "=",
|
|
4693
|
+
}, node);
|
|
4694
|
+
if (node.type === "AssignmentExpression") {
|
|
4695
|
+
node.left = id;
|
|
4696
|
+
}
|
|
4697
|
+
else {
|
|
4698
|
+
node.argument = id;
|
|
4699
|
+
}
|
|
4700
|
+
return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
|
|
4701
|
+
}
|
|
4702
|
+
if (event.type === "mod") {
|
|
4703
|
+
if (!event.decl) {
|
|
4704
|
+
throw new Error(`Unexpected null decl on mod event`);
|
|
4705
|
+
}
|
|
4706
|
+
let pending = pendingMap.get(stmt);
|
|
4707
|
+
if (!pending) {
|
|
4708
|
+
pendingMap.set(stmt, (pending = new Set()));
|
|
4709
|
+
}
|
|
4710
|
+
pending.add(event);
|
|
4711
|
+
}
|
|
4712
|
+
else {
|
|
4713
|
+
throw new Error(`Unexpected ${event.type} found`);
|
|
4714
|
+
}
|
|
4715
|
+
return ret;
|
|
4716
|
+
}, null);
|
|
4717
|
+
if (ret) {
|
|
4718
|
+
return ret;
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
const pending = pendingMap.get(node);
|
|
4722
|
+
if (node.type === "SequenceExpression") {
|
|
4723
|
+
if (pending) {
|
|
4724
|
+
throw new Error(`Unexpected pending list at SequenceExpression`);
|
|
4725
|
+
}
|
|
4726
|
+
for (let i = node.expressions.length; i--;) {
|
|
4727
|
+
const ni = node.expressions[i];
|
|
4728
|
+
if (ni.type === "SequenceExpression") {
|
|
4729
|
+
node.expressions.splice(i, 1, ...ni.expressions);
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
const applyPending = (results, locNode) => {
|
|
4734
|
+
const target = results.length === 1 && results[0].type === "BlockStatement"
|
|
4735
|
+
? results[0]
|
|
4736
|
+
: null;
|
|
4737
|
+
pendingMap.delete(node);
|
|
4738
|
+
pending.forEach((event) => {
|
|
4739
|
+
const decl = event.decl;
|
|
4740
|
+
const name = declMap.get(decl);
|
|
4741
|
+
if (!name) {
|
|
4742
|
+
throw new Error(`No replacement found for "${declFullName(decl)}"`);
|
|
4743
|
+
}
|
|
4744
|
+
if (!event.id) {
|
|
4745
|
+
throw new Error(`Missing id for mod event for "${declFullName(decl)}"`);
|
|
4746
|
+
}
|
|
4747
|
+
const rhs = withLocDeep(event.id, locNode, locNode);
|
|
4748
|
+
rhs.end = rhs.start;
|
|
4749
|
+
if (rhs.loc) {
|
|
4750
|
+
rhs.loc.end = rhs.loc.start;
|
|
4751
|
+
}
|
|
4752
|
+
const insertion = withLoc({
|
|
4753
|
+
type: "ExpressionStatement",
|
|
4754
|
+
expression: withLoc({
|
|
4755
|
+
type: "AssignmentExpression",
|
|
4756
|
+
left: ident(name, rhs),
|
|
4757
|
+
right: rhs,
|
|
4758
|
+
operator: "=",
|
|
4759
|
+
}, rhs),
|
|
4760
|
+
}, rhs);
|
|
4761
|
+
if (event.type === "mod" && event.before) {
|
|
4762
|
+
if (target) {
|
|
4763
|
+
target.body.unshift(insertion);
|
|
4764
|
+
}
|
|
4765
|
+
else {
|
|
4766
|
+
results.unshift(insertion);
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
else {
|
|
4770
|
+
results.push(insertion);
|
|
4771
|
+
}
|
|
4772
|
+
});
|
|
4773
|
+
return results.length === 1 ? null : results;
|
|
4774
|
+
};
|
|
4775
|
+
if (node.type === "ExpressionStatement" &&
|
|
4776
|
+
node.expression.type === "SequenceExpression") {
|
|
4777
|
+
const results = [];
|
|
4778
|
+
node.expression.expressions.forEach((expression) => {
|
|
4779
|
+
results.push({ ...node, expression });
|
|
4780
|
+
});
|
|
4781
|
+
if (!pending) {
|
|
4782
|
+
return results;
|
|
4783
|
+
}
|
|
4784
|
+
return applyPending(results, node);
|
|
4785
|
+
}
|
|
4786
|
+
if (pending) {
|
|
4787
|
+
return applyPending([node], node);
|
|
4788
|
+
}
|
|
4789
|
+
return null;
|
|
4790
|
+
});
|
|
4791
|
+
}
|
|
4792
|
+
|
|
4793
|
+
|
|
2783
4794
|
/***/ }),
|
|
2784
4795
|
|
|
2785
4796
|
/***/ 4859:
|
|
@@ -3089,7 +5100,7 @@ function filterDecls(decls, possible, name) {
|
|
|
3089
5100
|
const stack = d.stack;
|
|
3090
5101
|
possible.forEach((poss) => {
|
|
3091
5102
|
for (let i = stack.length; i--;) {
|
|
3092
|
-
const sn = stack[i];
|
|
5103
|
+
const sn = stack[i].sn;
|
|
3093
5104
|
if (sn.decls === poss.decls) {
|
|
3094
5105
|
if (!cur[0]) {
|
|
3095
5106
|
cur = [new Set(), new Set()];
|
|
@@ -3310,9 +5321,21 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
3310
5321
|
if (Array.isArray(decl) ||
|
|
3311
5322
|
(decl.type !== "MemberDecl" && decl.type !== "Unknown")) {
|
|
3312
5323
|
if (!clearEquiv) {
|
|
5324
|
+
/*
|
|
5325
|
+
* If we're not clearing the equivalencies then this update
|
|
5326
|
+
* must be applied to every element of the set
|
|
5327
|
+
*/
|
|
3313
5328
|
const v = blockState.get(decl);
|
|
3314
5329
|
if (v?.equivSet) {
|
|
3315
|
-
|
|
5330
|
+
let s = decl;
|
|
5331
|
+
do {
|
|
5332
|
+
const next = blockState.get(s);
|
|
5333
|
+
if (!next || !next.equivSet) {
|
|
5334
|
+
throw new Error(`Inconsistent equivSet for ${tsKey(decl)}: missing value for ${tsKey(s)}`);
|
|
5335
|
+
}
|
|
5336
|
+
blockState.set(s, { ...next, curType: value });
|
|
5337
|
+
s = next.equivSet.next;
|
|
5338
|
+
} while (s !== decl);
|
|
3316
5339
|
return;
|
|
3317
5340
|
}
|
|
3318
5341
|
}
|
|
@@ -3418,7 +5441,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
3418
5441
|
return tmpState;
|
|
3419
5442
|
}
|
|
3420
5443
|
if (isExact(right) &&
|
|
3421
|
-
right.type &
|
|
5444
|
+
right.type & SingletonTypeTagsConst &&
|
|
3422
5445
|
left.type & right.type) {
|
|
3423
5446
|
// left is not equal to right, and right is one of the
|
|
3424
5447
|
// singleton types; so we can remove that type from left.
|
|
@@ -4336,7 +6359,7 @@ function common_types(left, right, allowed) {
|
|
|
4336
6359
|
}
|
|
4337
6360
|
if (lt & 8 /* TypeTag.Number */) {
|
|
4338
6361
|
result |=
|
|
4339
|
-
rt & 6 /* TypeTag.Boolean */ ? 6 /* TypeTag.Boolean */ : rt & ~
|
|
6362
|
+
rt & 6 /* TypeTag.Boolean */ ? 6 /* TypeTag.Boolean */ : rt & ~SingletonTypeTagsConst;
|
|
4340
6363
|
}
|
|
4341
6364
|
if (lt & 16 /* TypeTag.Long */) {
|
|
4342
6365
|
if (rt & 6 /* TypeTag.Boolean */) {
|
|
@@ -4746,7 +6769,7 @@ function calleeObjectType(istate, callee) {
|
|
|
4746
6769
|
}
|
|
4747
6770
|
if (callee.type === "Identifier" && istate.func) {
|
|
4748
6771
|
const func = istate.func;
|
|
4749
|
-
const [self] = func.stack.slice(-1);
|
|
6772
|
+
const [{ sn: self }] = func.stack.slice(-1);
|
|
4750
6773
|
return typeFromTypeStateNode(istate.state, self, (func.attributes & StateNodeAttributes.STATIC) !== 0 ||
|
|
4751
6774
|
self.type !== "ClassDeclaration");
|
|
4752
6775
|
}
|
|
@@ -4878,14 +6901,14 @@ function checkCallArgs(istate, node, callees, args) {
|
|
|
4878
6901
|
return resultType;
|
|
4879
6902
|
}
|
|
4880
6903
|
function isOverride(cur, funcs) {
|
|
4881
|
-
const cls = cur.stack?.[cur.stack.length - 1];
|
|
6904
|
+
const cls = cur.stack?.[cur.stack.length - 1]?.sn;
|
|
4882
6905
|
if (cls?.type === "ClassDeclaration" && cls.superClasses) {
|
|
4883
6906
|
const supers = getSuperClasses(cls);
|
|
4884
6907
|
if (supers &&
|
|
4885
6908
|
some(funcs, (func) => {
|
|
4886
6909
|
if (func === cur)
|
|
4887
6910
|
return false;
|
|
4888
|
-
const fcls = func.stack?.[func.stack.length - 1];
|
|
6911
|
+
const fcls = func.stack?.[func.stack.length - 1].sn;
|
|
4889
6912
|
return fcls ? supers.has(fcls) : false;
|
|
4890
6913
|
})) {
|
|
4891
6914
|
return true;
|
|
@@ -5659,7 +7682,7 @@ function evaluateNode(istate, node) {
|
|
|
5659
7682
|
case "ThisExpression": {
|
|
5660
7683
|
const self = (() => {
|
|
5661
7684
|
for (let i = state.stack.length; i--;) {
|
|
5662
|
-
const si = state.stack[i];
|
|
7685
|
+
const si = state.stack[i].sn;
|
|
5663
7686
|
if (si.type === "ClassDeclaration") {
|
|
5664
7687
|
const klass = { type: 16384 /* TypeTag.Class */, value: si };
|
|
5665
7688
|
if ((istate.func?.attributes || 0) & StateNodeAttributes.STATIC) {
|
|
@@ -5869,7 +7892,7 @@ function evaluateNode(istate, node) {
|
|
|
5869
7892
|
if (node.init) {
|
|
5870
7893
|
const init = popIstate(istate, node.init);
|
|
5871
7894
|
if (node.id.type === "BinaryExpression" && istate.typeChecker) {
|
|
5872
|
-
const top = istate.state.
|
|
7895
|
+
const top = istate.state.top().sn;
|
|
5873
7896
|
if (top.type !== "BlockStatement") {
|
|
5874
7897
|
const declType = typeFromTypespec(istate.state, node.id.right);
|
|
5875
7898
|
if (!istate.typeChecker(init.value, declType)) {
|
|
@@ -6458,58 +8481,692 @@ function restrictExactTypesByEquality(a, b) {
|
|
|
6458
8481
|
default:
|
|
6459
8482
|
unhandledType(a);
|
|
6460
8483
|
}
|
|
6461
|
-
return { type: 0 /* TypeTag.Never */ };
|
|
8484
|
+
return { type: 0 /* TypeTag.Never */ };
|
|
8485
|
+
}
|
|
8486
|
+
function restrictByEqualityByComponent(a, b) {
|
|
8487
|
+
let bits = a.type;
|
|
8488
|
+
if (a.value == null && (b.type & bits) === b.type) {
|
|
8489
|
+
// shortcut:
|
|
8490
|
+
// if b.type is contained in a.type, and a has no
|
|
8491
|
+
// specialization, the result is just b.
|
|
8492
|
+
return b;
|
|
8493
|
+
}
|
|
8494
|
+
let br = null;
|
|
8495
|
+
do {
|
|
8496
|
+
const next = bits & (bits - 1);
|
|
8497
|
+
const bit = bits - next;
|
|
8498
|
+
const brt = restrictExactTypesByEquality({
|
|
8499
|
+
type: bit,
|
|
8500
|
+
value: getUnionComponent(a, bit) || undefined,
|
|
8501
|
+
}, b);
|
|
8502
|
+
if (brt.type !== 0 /* TypeTag.Never */) {
|
|
8503
|
+
if (!br) {
|
|
8504
|
+
br = cloneType(brt);
|
|
8505
|
+
}
|
|
8506
|
+
else {
|
|
8507
|
+
unionInto(br, brt);
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
bits = next;
|
|
8511
|
+
} while (bits);
|
|
8512
|
+
if (!br) {
|
|
8513
|
+
return { type: 0 /* TypeTag.Never */ };
|
|
8514
|
+
}
|
|
8515
|
+
return br;
|
|
8516
|
+
}
|
|
8517
|
+
/*
|
|
8518
|
+
* Given that a == b, reutnr what we can deduce about b's
|
|
8519
|
+
* type.
|
|
8520
|
+
*
|
|
8521
|
+
* Note that this is similar to intersection. In many cases, it
|
|
8522
|
+
* is intersection. eg if a is the type (Null or "Hello" or Menu)
|
|
8523
|
+
* then we know b's type must be a subtype of intersection(a, b).
|
|
8524
|
+
*
|
|
8525
|
+
* But eg if it was a == 5, and a is known to be Char, String,
|
|
8526
|
+
* Number or Float, we can only eliminate String, because
|
|
8527
|
+
* 5.0 == 5, and 5.toChar() == 5.
|
|
8528
|
+
*/
|
|
8529
|
+
function restrictByEquality(a, b) {
|
|
8530
|
+
if (a.type === 0 /* TypeTag.Never */)
|
|
8531
|
+
return a;
|
|
8532
|
+
if (isExact(a)) {
|
|
8533
|
+
return restrictExactTypesByEquality(a, b);
|
|
8534
|
+
}
|
|
8535
|
+
return restrictByEqualityByComponent(a, b);
|
|
8536
|
+
}
|
|
8537
|
+
|
|
8538
|
+
|
|
8539
|
+
/***/ }),
|
|
8540
|
+
|
|
8541
|
+
/***/ 3687:
|
|
8542
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
8543
|
+
|
|
8544
|
+
"use strict";
|
|
8545
|
+
/* unused harmony exports optimizeFunction, beforeEvaluate, afterEvaluate, tryDeEnumerate */
|
|
8546
|
+
/* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(333);
|
|
8547
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
8548
|
+
/* harmony import */ var _type_flow__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4859);
|
|
8549
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6906);
|
|
8550
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_3__);
|
|
8551
|
+
/* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7161);
|
|
8552
|
+
/* harmony import */ var _interp_binary__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4706);
|
|
8553
|
+
/* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7255);
|
|
8554
|
+
|
|
8555
|
+
|
|
8556
|
+
|
|
8557
|
+
|
|
8558
|
+
|
|
8559
|
+
|
|
8560
|
+
|
|
8561
|
+
function optimizeFunction(state, func) {
|
|
8562
|
+
const istate = buildTypeInfo(state, func, true);
|
|
8563
|
+
if (!istate)
|
|
8564
|
+
return;
|
|
8565
|
+
evaluate({
|
|
8566
|
+
...istate,
|
|
8567
|
+
pre(node) {
|
|
8568
|
+
return beforeEvaluate(this, node);
|
|
8569
|
+
},
|
|
8570
|
+
post(node) {
|
|
8571
|
+
return afterEvaluate(this, node);
|
|
8572
|
+
},
|
|
8573
|
+
}, func.node.body);
|
|
8574
|
+
}
|
|
8575
|
+
function beforeEvaluate(istate, node) {
|
|
8576
|
+
switch (node.type) {
|
|
8577
|
+
case "ConditionalExpression": {
|
|
8578
|
+
let alternate = tryPop(istate, node.alternate);
|
|
8579
|
+
let consequent = tryPop(istate, node.consequent);
|
|
8580
|
+
const test = popIstate(istate, node.test);
|
|
8581
|
+
const result = mustBeTrue(test.value)
|
|
8582
|
+
? true
|
|
8583
|
+
: mustBeFalse(test.value)
|
|
8584
|
+
? false
|
|
8585
|
+
: null;
|
|
8586
|
+
if (result !== null) {
|
|
8587
|
+
if (!test.embeddedEffects) {
|
|
8588
|
+
const arg = result ? consequent : alternate;
|
|
8589
|
+
istate.stack.push(arg);
|
|
8590
|
+
return result ? node.consequent : node.alternate;
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
if (node.alternate &&
|
|
8594
|
+
node.test.type === "UnaryExpression" &&
|
|
8595
|
+
node.test.operator === "!" &&
|
|
8596
|
+
test.value.type === 6 /* TypeTag.Boolean */) {
|
|
8597
|
+
const alternateNode = node.alternate;
|
|
8598
|
+
node.alternate = node.consequent;
|
|
8599
|
+
node.consequent = alternateNode;
|
|
8600
|
+
const tmp = alternate;
|
|
8601
|
+
alternate = consequent;
|
|
8602
|
+
consequent = tmp;
|
|
8603
|
+
test.node = node.test = node.test.argument;
|
|
8604
|
+
}
|
|
8605
|
+
if (test.value.type === 6 /* TypeTag.Boolean */ &&
|
|
8606
|
+
((consequent.value.type === 4 /* TypeTag.True */ &&
|
|
8607
|
+
alternate.value.type === 2 /* TypeTag.False */) ||
|
|
8608
|
+
(consequent.value.type === 2 /* TypeTag.False */ &&
|
|
8609
|
+
alternate.value.type === 4 /* TypeTag.True */)) &&
|
|
8610
|
+
!consequent.embeddedEffects &&
|
|
8611
|
+
!alternate.embeddedEffects) {
|
|
8612
|
+
if (consequent.value.type === 2 /* TypeTag.False */) {
|
|
8613
|
+
test.node = wrap({
|
|
8614
|
+
type: "UnaryExpression",
|
|
8615
|
+
operator: "!",
|
|
8616
|
+
argument: node.test,
|
|
8617
|
+
prefix: true,
|
|
8618
|
+
}, test.node.loc);
|
|
8619
|
+
}
|
|
8620
|
+
istate.stack.push(test);
|
|
8621
|
+
return test.node;
|
|
8622
|
+
}
|
|
8623
|
+
istate.stack.push(test, consequent, alternate);
|
|
8624
|
+
break;
|
|
8625
|
+
}
|
|
8626
|
+
case "IfStatement": {
|
|
8627
|
+
if (node.alternate &&
|
|
8628
|
+
node.alternate.type === "BlockStatement" &&
|
|
8629
|
+
!node.alternate.body.length) {
|
|
8630
|
+
delete node.alternate;
|
|
8631
|
+
}
|
|
8632
|
+
const test = popIstate(istate, node.test);
|
|
8633
|
+
if (!node.alternate &&
|
|
8634
|
+
node.consequent.type === "BlockStatement" &&
|
|
8635
|
+
!node.consequent.body.length) {
|
|
8636
|
+
const ret = withLoc(node.consequent, node.test, node.test);
|
|
8637
|
+
const u = test.embeddedEffects && unused(istate.state, node.test);
|
|
8638
|
+
if (u) {
|
|
8639
|
+
node.consequent.body = u;
|
|
8640
|
+
}
|
|
8641
|
+
return ret;
|
|
8642
|
+
}
|
|
8643
|
+
const result = mustBeTrue(test.value)
|
|
8644
|
+
? true
|
|
8645
|
+
: mustBeFalse(test.value)
|
|
8646
|
+
? false
|
|
8647
|
+
: null;
|
|
8648
|
+
if (result !== null) {
|
|
8649
|
+
const rep = result ? node.consequent : node.alternate || false;
|
|
8650
|
+
if (!test.embeddedEffects) {
|
|
8651
|
+
return rep;
|
|
8652
|
+
}
|
|
8653
|
+
const estmt = wrap({ type: "ExpressionStatement", expression: node.test }, node.loc);
|
|
8654
|
+
if (!rep) {
|
|
8655
|
+
return estmt;
|
|
8656
|
+
}
|
|
8657
|
+
if (rep.type === "BlockStatement") {
|
|
8658
|
+
rep.body.unshift(estmt);
|
|
8659
|
+
return rep;
|
|
8660
|
+
}
|
|
8661
|
+
}
|
|
8662
|
+
if (node.alternate &&
|
|
8663
|
+
node.test.type === "UnaryExpression" &&
|
|
8664
|
+
node.test.operator === "!" &&
|
|
8665
|
+
test.value.type === 6 /* TypeTag.Boolean */) {
|
|
8666
|
+
const alternate = node.alternate;
|
|
8667
|
+
node.alternate = node.consequent;
|
|
8668
|
+
node.consequent = alternate;
|
|
8669
|
+
test.node = node.test = node.test.argument;
|
|
8670
|
+
}
|
|
8671
|
+
istate.stack.push(test);
|
|
8672
|
+
break;
|
|
8673
|
+
}
|
|
8674
|
+
case "WhileStatement":
|
|
8675
|
+
case "DoWhileStatement": {
|
|
8676
|
+
const test = popIstate(istate, node.test);
|
|
8677
|
+
if (!test.embeddedEffects) {
|
|
8678
|
+
if (mustBeFalse(test.value)) {
|
|
8679
|
+
return node.type === "WhileStatement" ? false : node.body;
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
istate.stack.push(test);
|
|
8683
|
+
break;
|
|
8684
|
+
}
|
|
8685
|
+
case "BinaryExpression":
|
|
8686
|
+
if (node.operator === "has" &&
|
|
8687
|
+
node.right.type === "UnaryExpression" &&
|
|
8688
|
+
node.right.operator === ":") {
|
|
8689
|
+
const [left, right] = istate.stack.slice(-2);
|
|
8690
|
+
if (left.embeddedEffects ||
|
|
8691
|
+
right.embeddedEffects ||
|
|
8692
|
+
!hasValue(left.value) ||
|
|
8693
|
+
!hasValue(right.value) ||
|
|
8694
|
+
!(left.value.type & (4096 /* TypeTag.Module */ | 16384 /* TypeTag.Class */))) {
|
|
8695
|
+
break;
|
|
8696
|
+
}
|
|
8697
|
+
const id = node.right.argument;
|
|
8698
|
+
if (every(left.value.value, (m) => {
|
|
8699
|
+
if (hasProperty(m.decls, id.name))
|
|
8700
|
+
return false;
|
|
8701
|
+
// This is overkill, since we've already looked up
|
|
8702
|
+
// node.left, but the actual lookup rules are complicated,
|
|
8703
|
+
// and embedded within state.lookup; so just defer to that.
|
|
8704
|
+
return (istate.state.lookup({
|
|
8705
|
+
type: "MemberExpression",
|
|
8706
|
+
object: node.left,
|
|
8707
|
+
property: id,
|
|
8708
|
+
computed: false,
|
|
8709
|
+
})[1] == null);
|
|
8710
|
+
})) {
|
|
8711
|
+
popIstate(istate, node.right);
|
|
8712
|
+
popIstate(istate, node.left);
|
|
8713
|
+
const rep = wrap({ type: "Literal", value: false, raw: "false" }, node.loc);
|
|
8714
|
+
istate.stack.push({
|
|
8715
|
+
value: { type: 2 /* TypeTag.False */ },
|
|
8716
|
+
embeddedEffects: false,
|
|
8717
|
+
node: rep,
|
|
8718
|
+
});
|
|
8719
|
+
return rep;
|
|
8720
|
+
}
|
|
8721
|
+
}
|
|
8722
|
+
else {
|
|
8723
|
+
if (node.operator !== "as") {
|
|
8724
|
+
const left = tryDeEnumerate(istate, node.left, -2);
|
|
8725
|
+
if (left)
|
|
8726
|
+
node.left = left;
|
|
8727
|
+
const right = tryDeEnumerate(istate, node.right, -1);
|
|
8728
|
+
if (right)
|
|
8729
|
+
node.right = right;
|
|
8730
|
+
}
|
|
8731
|
+
const rep = tryCommuteAndAssociate(istate, node);
|
|
8732
|
+
if (rep)
|
|
8733
|
+
return rep;
|
|
8734
|
+
}
|
|
8735
|
+
break;
|
|
8736
|
+
case "LogicalExpression": {
|
|
8737
|
+
const right = tryPop(istate, node.right);
|
|
8738
|
+
const left = popIstate(istate, node.left);
|
|
8739
|
+
const isAnd = node.operator === "&&" || node.operator === "and";
|
|
8740
|
+
if (isAnd ? mustBeFalse(left.value) : mustBeTrue(left.value)) {
|
|
8741
|
+
istate.stack.push(left);
|
|
8742
|
+
return node.left;
|
|
8743
|
+
}
|
|
8744
|
+
if (
|
|
8745
|
+
// bail if left could be anything other than bool/integer
|
|
8746
|
+
(left.value.type &
|
|
8747
|
+
(6 /* TypeTag.Boolean */ | 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */)) !==
|
|
8748
|
+
left.value.type ||
|
|
8749
|
+
// bail if left could be boolean AND it could be integer
|
|
8750
|
+
(left.value.type & 6 /* TypeTag.Boolean */ &&
|
|
8751
|
+
left.value.type & (8 /* TypeTag.Number */ | 16 /* TypeTag.Long */)) ||
|
|
8752
|
+
// bail if right doesn't match left
|
|
8753
|
+
(right.value.type &
|
|
8754
|
+
(left.value.type & 6 /* TypeTag.Boolean */
|
|
8755
|
+
? 6 /* TypeTag.Boolean */
|
|
8756
|
+
: 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */)) !==
|
|
8757
|
+
right.value.type) {
|
|
8758
|
+
istate.stack.push(left);
|
|
8759
|
+
istate.stack.push(right);
|
|
8760
|
+
break;
|
|
8761
|
+
}
|
|
8762
|
+
if (right.value.type === (isAnd ? 4 /* TypeTag.True */ : 2 /* TypeTag.False */) &&
|
|
8763
|
+
!right.embeddedEffects &&
|
|
8764
|
+
(left.value.type & 6 /* TypeTag.Boolean */) === left.value.type) {
|
|
8765
|
+
istate.stack.push(left);
|
|
8766
|
+
return node.left;
|
|
8767
|
+
}
|
|
8768
|
+
if (right.value.type === (isAnd ? 2 /* TypeTag.False */ : 4 /* TypeTag.True */) &&
|
|
8769
|
+
!left.embeddedEffects &&
|
|
8770
|
+
(left.value.type & 6 /* TypeTag.Boolean */) === left.value.type) {
|
|
8771
|
+
istate.stack.push(right);
|
|
8772
|
+
return node.right;
|
|
8773
|
+
}
|
|
8774
|
+
istate.stack.push(left);
|
|
8775
|
+
istate.stack.push(right);
|
|
8776
|
+
if (isAnd ? !mustBeTrue(left.value) : !mustBeFalse(left.value)) {
|
|
8777
|
+
break;
|
|
8778
|
+
}
|
|
8779
|
+
if (!(left.value.type & ~6 /* TypeTag.Boolean */)) {
|
|
8780
|
+
// the left type is boolean, so the
|
|
8781
|
+
// `&` or `|` would be a no-op. We
|
|
8782
|
+
// need to check that its side-effect
|
|
8783
|
+
// free. Just cheap checks for now.
|
|
8784
|
+
if (!left.embeddedEffects) {
|
|
8785
|
+
istate.stack.splice(-2, 2, right);
|
|
8786
|
+
return node.right;
|
|
8787
|
+
}
|
|
8788
|
+
}
|
|
8789
|
+
const rep = node;
|
|
8790
|
+
rep.type = "BinaryExpression";
|
|
8791
|
+
rep.operator = isAnd ? "&" : "|";
|
|
8792
|
+
break;
|
|
8793
|
+
}
|
|
8794
|
+
case "ForStatement": {
|
|
8795
|
+
if (node.update?.type === "Literal" ||
|
|
8796
|
+
(node.update?.type === "SequenceExpression" &&
|
|
8797
|
+
node.update.expressions.length === 0)) {
|
|
8798
|
+
popIstate(istate, node.update);
|
|
8799
|
+
delete node.update;
|
|
8800
|
+
}
|
|
8801
|
+
if (node.init?.type === "Literal" ||
|
|
8802
|
+
(node.init?.type === "SequenceExpression" &&
|
|
8803
|
+
node.init.expressions.length === 0)) {
|
|
8804
|
+
delete node.init;
|
|
8805
|
+
const depth = -1 - (node.update ? 1 : 0) - (node.test ? 1 : 0);
|
|
8806
|
+
istate.stack.splice(depth, 1);
|
|
8807
|
+
}
|
|
8808
|
+
break;
|
|
8809
|
+
}
|
|
8810
|
+
case "BlockStatement": {
|
|
8811
|
+
for (let i = node.body.length; i--;) {
|
|
8812
|
+
const stmt = node.body[i];
|
|
8813
|
+
if (stmt.type === "VariableDeclaration" && !stmt.declarations.length) {
|
|
8814
|
+
node.body.splice(i, 1);
|
|
8815
|
+
}
|
|
8816
|
+
else if (stmt.type === "BlockStatement" &&
|
|
8817
|
+
stmt.body.every((s) => s.type !== "VariableDeclaration")) {
|
|
8818
|
+
node.body.splice(i, 1, ...stmt.body);
|
|
8819
|
+
}
|
|
8820
|
+
}
|
|
8821
|
+
break;
|
|
8822
|
+
}
|
|
8823
|
+
case "SequenceExpression": {
|
|
8824
|
+
for (let i = node.expressions.length; i--;) {
|
|
8825
|
+
const expr = node.expressions[i];
|
|
8826
|
+
if (expr.type === "Literal") {
|
|
8827
|
+
istate.stack.splice(i - node.expressions.length, 1);
|
|
8828
|
+
node.expressions.splice(i, 1);
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
break;
|
|
8832
|
+
}
|
|
8833
|
+
case "AssignmentExpression": {
|
|
8834
|
+
if (node.operator === "=") {
|
|
8835
|
+
let selfAssign = false;
|
|
8836
|
+
if (node.left.type === "Identifier" &&
|
|
8837
|
+
node.right.type === "Identifier" &&
|
|
8838
|
+
node.left.name === node.right.name) {
|
|
8839
|
+
selfAssign = true;
|
|
8840
|
+
}
|
|
8841
|
+
else {
|
|
8842
|
+
const [left, right] = istate.stack.slice(-2);
|
|
8843
|
+
if (!left.embeddedEffects &&
|
|
8844
|
+
!right.embeddedEffects &&
|
|
8845
|
+
mustBeIdentical(left.value, right.value)) {
|
|
8846
|
+
selfAssign = true;
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
if (selfAssign) {
|
|
8850
|
+
popIstate(istate, node.right);
|
|
8851
|
+
popIstate(istate, node.left);
|
|
8852
|
+
const rep = withLoc({ type: "Literal", value: null, raw: "null" }, node, node);
|
|
8853
|
+
istate.stack.push({
|
|
8854
|
+
value: { type: 1 /* TypeTag.Null */ },
|
|
8855
|
+
embeddedEffects: false,
|
|
8856
|
+
node: rep,
|
|
8857
|
+
});
|
|
8858
|
+
return rep;
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8861
|
+
break;
|
|
8862
|
+
}
|
|
8863
|
+
}
|
|
8864
|
+
return null;
|
|
8865
|
+
}
|
|
8866
|
+
function afterEvaluate(istate, node) {
|
|
8867
|
+
if (isExpression(node) && node.type !== "Literal") {
|
|
8868
|
+
const top = istate.stack[istate.stack.length - 1];
|
|
8869
|
+
if (!top.embeddedEffects && hasValue(top.value)) {
|
|
8870
|
+
const rep = mcExprFromType(top.value);
|
|
8871
|
+
if (rep) {
|
|
8872
|
+
top.node = rep;
|
|
8873
|
+
return withLoc(rep, node, node);
|
|
8874
|
+
}
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
return null;
|
|
8878
|
+
}
|
|
8879
|
+
function identity(istate, node, left, right, allowedTypes, target) {
|
|
8880
|
+
if (hasValue(right.value) &&
|
|
8881
|
+
right.value.type & allowedTypes &&
|
|
8882
|
+
!(left.value.type & ~allowedTypes) &&
|
|
8883
|
+
Number(right.value.value) === target) {
|
|
8884
|
+
// a +/- 0 => a
|
|
8885
|
+
// but we still need to check that the type of the zero
|
|
8886
|
+
// doesn't change the type of the result.
|
|
8887
|
+
if (right.value.type === 8 /* TypeTag.Number */ ||
|
|
8888
|
+
(right.value.type === 16 /* TypeTag.Long */ &&
|
|
8889
|
+
!(left.value.type & ~(16 /* TypeTag.Long */ | 64 /* TypeTag.Double */))) ||
|
|
8890
|
+
(right.value.type === 32 /* TypeTag.Float */ &&
|
|
8891
|
+
!(left.value.type & ~(32 /* TypeTag.Float */ | 64 /* TypeTag.Double */))) ||
|
|
8892
|
+
(right.value.type === 64 /* TypeTag.Double */ &&
|
|
8893
|
+
left.value.type === 64 /* TypeTag.Double */)) {
|
|
8894
|
+
istate.stack.pop();
|
|
8895
|
+
return node.left;
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8898
|
+
return null;
|
|
8899
|
+
}
|
|
8900
|
+
function zero(istate, node, left, right, allowedTypes, target) {
|
|
8901
|
+
if (hasValue(right.value) &&
|
|
8902
|
+
right.value.type & allowedTypes &&
|
|
8903
|
+
!(left.value.type & ~allowedTypes) &&
|
|
8904
|
+
Number(right.value.value) === target) {
|
|
8905
|
+
// a * 0 => 0
|
|
8906
|
+
// but we still need to check that the type of a
|
|
8907
|
+
// doesn't change the type of the zero.
|
|
8908
|
+
if ((right.value.type === 8 /* TypeTag.Number */ &&
|
|
8909
|
+
left.value.type === 8 /* TypeTag.Number */) ||
|
|
8910
|
+
(right.value.type === 16 /* TypeTag.Long */ &&
|
|
8911
|
+
!(left.value.type & ~(16 /* TypeTag.Long */ | 8 /* TypeTag.Number */))) ||
|
|
8912
|
+
(right.value.type === 32 /* TypeTag.Float */ &&
|
|
8913
|
+
!(left.value.type & ~(32 /* TypeTag.Float */ | 8 /* TypeTag.Number */))) ||
|
|
8914
|
+
right.value.type === 64 /* TypeTag.Double */) {
|
|
8915
|
+
istate.stack.splice(-2, 1);
|
|
8916
|
+
return node.right;
|
|
8917
|
+
}
|
|
8918
|
+
}
|
|
8919
|
+
return null;
|
|
8920
|
+
}
|
|
8921
|
+
function tryIdentity(istate, node, left, right) {
|
|
8922
|
+
switch (node.operator) {
|
|
8923
|
+
case "+":
|
|
8924
|
+
case "-": {
|
|
8925
|
+
const rep = identity(istate, node, left, right, 120 /* TypeTag.Numeric */, 0);
|
|
8926
|
+
if (rep)
|
|
8927
|
+
return rep;
|
|
8928
|
+
if (node.right.type === "UnaryExpression" &&
|
|
8929
|
+
node.right.operator === "-") {
|
|
8930
|
+
// We can convert a +/- -b to a -/+ b, but we have to know
|
|
8931
|
+
// something about the types. eg if b is Number, with the
|
|
8932
|
+
// value -2^31, negating b will leave the value as -2^31.
|
|
8933
|
+
// This doesn't matter if the a is also Number, but if
|
|
8934
|
+
// a might be Long, Float or Double, we would change the
|
|
8935
|
+
// result. Similarly for Long and -2^63
|
|
8936
|
+
if (!((left.value.type | right.value.type) & ~120 /* TypeTag.Numeric */) &&
|
|
8937
|
+
(right.value.type & 8 /* TypeTag.Number */ // Negating -2^31 goes wrong if left is a wider type
|
|
8938
|
+
? !(left.value.type &
|
|
8939
|
+
(16 /* TypeTag.Long */ | 32 /* TypeTag.Float */ | 64 /* TypeTag.Double */))
|
|
8940
|
+
: right.value.type & 16 /* TypeTag.Long */ // Negating -2^63 goes wrong if left is a float/double
|
|
8941
|
+
? !(left.value.type & (32 /* TypeTag.Float */ | 64 /* TypeTag.Double */))
|
|
8942
|
+
: true)) {
|
|
8943
|
+
right.value = evaluateBinaryTypes("-", { type: 8 /* TypeTag.Number */, value: 0 }, right.value);
|
|
8944
|
+
right.node = node.right.argument;
|
|
8945
|
+
node.right = right.node;
|
|
8946
|
+
node.operator = node.operator === "+" ? "-" : "+";
|
|
8947
|
+
}
|
|
8948
|
+
}
|
|
8949
|
+
break;
|
|
8950
|
+
}
|
|
8951
|
+
case "*": {
|
|
8952
|
+
const rep = zero(istate, node, left, right, 120 /* TypeTag.Numeric */, 0);
|
|
8953
|
+
if (rep)
|
|
8954
|
+
return rep;
|
|
8955
|
+
// fall through
|
|
8956
|
+
}
|
|
8957
|
+
case "/": {
|
|
8958
|
+
const rep = identity(istate, node, left, right, 120 /* TypeTag.Numeric */, 1);
|
|
8959
|
+
if (rep)
|
|
8960
|
+
return rep;
|
|
8961
|
+
break;
|
|
8962
|
+
}
|
|
8963
|
+
case "|": {
|
|
8964
|
+
const rep = zero(istate, node, left, right, 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */, -1);
|
|
8965
|
+
if (rep)
|
|
8966
|
+
return rep;
|
|
8967
|
+
// fall through
|
|
8968
|
+
}
|
|
8969
|
+
case "^": {
|
|
8970
|
+
const rep = identity(istate, node, left, right, 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */, 0);
|
|
8971
|
+
if (rep)
|
|
8972
|
+
return rep;
|
|
8973
|
+
break;
|
|
8974
|
+
}
|
|
8975
|
+
case "&": {
|
|
8976
|
+
const rep = zero(istate, node, left, right, 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */, 0) ||
|
|
8977
|
+
identity(istate, node, left, right, 8 /* TypeTag.Number */ | 16 /* TypeTag.Long */, -1);
|
|
8978
|
+
if (rep)
|
|
8979
|
+
return rep;
|
|
8980
|
+
break;
|
|
8981
|
+
}
|
|
8982
|
+
}
|
|
8983
|
+
return null;
|
|
6462
8984
|
}
|
|
6463
|
-
function
|
|
6464
|
-
let
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
// specialization, the result is just b.
|
|
6469
|
-
return b;
|
|
8985
|
+
function tryCommuteAndAssociate(istate, node) {
|
|
8986
|
+
let [left, right] = istate.stack.slice(-2);
|
|
8987
|
+
// no need to do anything if both sides are constants
|
|
8988
|
+
if (!right || (hasValue(left.value) && hasValue(right.value))) {
|
|
8989
|
+
return null;
|
|
6470
8990
|
}
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
if (brt.type !== 0 /* TypeTag.Never */) {
|
|
6480
|
-
if (!br) {
|
|
6481
|
-
br = cloneType(brt);
|
|
8991
|
+
switch (node.operator) {
|
|
8992
|
+
case "+":
|
|
8993
|
+
// Addition is only commutative/associative if both arguments
|
|
8994
|
+
// are numeric, or one argument is Number, and the other is Char
|
|
8995
|
+
if (left.value.type & ~(120 /* TypeTag.Numeric */ | 128 /* TypeTag.Char */) ||
|
|
8996
|
+
right.value.type & ~(120 /* TypeTag.Numeric */ | 128 /* TypeTag.Char */) ||
|
|
8997
|
+
left.value.type & right.value.type & 128 /* TypeTag.Char */) {
|
|
8998
|
+
break;
|
|
6482
8999
|
}
|
|
6483
|
-
|
|
6484
|
-
|
|
9000
|
+
// fallthrough
|
|
9001
|
+
case "*":
|
|
9002
|
+
case "&":
|
|
9003
|
+
case "|":
|
|
9004
|
+
case "^":
|
|
9005
|
+
// flip the left argument to the right if the left has
|
|
9006
|
+
// a known value, but the right does not, or if the
|
|
9007
|
+
// top operator is additive, and the left operand is
|
|
9008
|
+
// negated, and the right operand is not.
|
|
9009
|
+
if (!left.embeddedEffects &&
|
|
9010
|
+
(hasValue(left.value) ||
|
|
9011
|
+
(!right.embeddedEffects &&
|
|
9012
|
+
node.operator === "+" &&
|
|
9013
|
+
node.left.type === "UnaryExpression" &&
|
|
9014
|
+
node.left.operator === "-" &&
|
|
9015
|
+
(node.right.type !== "UnaryExpression" ||
|
|
9016
|
+
node.right.operator !== "-")))) {
|
|
9017
|
+
const l = node.left;
|
|
9018
|
+
node.left = node.right;
|
|
9019
|
+
node.right = l;
|
|
9020
|
+
istate.stack.splice(-2, 2, right, left);
|
|
9021
|
+
const r = right;
|
|
9022
|
+
right = left;
|
|
9023
|
+
left = r;
|
|
9024
|
+
}
|
|
9025
|
+
// fallthrough
|
|
9026
|
+
case "-":
|
|
9027
|
+
if (tryReAssociate(istate, node, left, right)) {
|
|
9028
|
+
[left, right] = istate.stack.slice(-2);
|
|
6485
9029
|
}
|
|
6486
|
-
}
|
|
6487
|
-
bits = next;
|
|
6488
|
-
} while (bits);
|
|
6489
|
-
if (!br) {
|
|
6490
|
-
return { type: 0 /* TypeTag.Never */ };
|
|
6491
9030
|
}
|
|
6492
|
-
return
|
|
9031
|
+
return tryIdentity(istate, node, left, right);
|
|
6493
9032
|
}
|
|
6494
9033
|
/*
|
|
6495
|
-
*
|
|
6496
|
-
*
|
|
9034
|
+
* Try to reorder (a op K1) op K2 => a op (K1 op K2),
|
|
9035
|
+
* and fold K1 op K2.
|
|
6497
9036
|
*
|
|
6498
|
-
*
|
|
6499
|
-
*
|
|
6500
|
-
*
|
|
9037
|
+
* Failing that,
|
|
9038
|
+
* Try to reorder (a op K1) op (b op K2) as
|
|
9039
|
+
* (a op b) op (K1 op K2), and fold K1 op K2.
|
|
6501
9040
|
*
|
|
6502
|
-
*
|
|
6503
|
-
*
|
|
6504
|
-
*
|
|
9041
|
+
* Failing that,
|
|
9042
|
+
* Try to reorder (a op K) op b => (a op b) op K
|
|
9043
|
+
* so that constants float up and to the right.
|
|
9044
|
+
* This helps because now ((a op K1) op b) op K2
|
|
9045
|
+
* becomes ((a op b) op K1) op K2, and we can
|
|
9046
|
+
* fold K1 op K2 by the first transformation.
|
|
9047
|
+
*
|
|
9048
|
+
* Floating point arithmetic isn't really associative
|
|
9049
|
+
* though, so we mostly suppress this when Floats
|
|
9050
|
+
* and Doubles may be involved; except that
|
|
9051
|
+
* (a + K1) + K2 can be safely converted to
|
|
9052
|
+
* a + (K1 + K2) if K1 and K2 have the same sign.
|
|
6505
9053
|
*/
|
|
6506
|
-
function
|
|
6507
|
-
if (
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
9054
|
+
function tryReAssociate(istate, node, left, right) {
|
|
9055
|
+
if (node.left.type !== "BinaryExpression" ||
|
|
9056
|
+
(node.left.operator !== node.operator &&
|
|
9057
|
+
!(node.operator === "+" && node.left.operator === "-") &&
|
|
9058
|
+
!(node.operator === "-" && node.left.operator === "+"))) {
|
|
9059
|
+
return false;
|
|
6511
9060
|
}
|
|
6512
|
-
|
|
9061
|
+
const lr = getLiteralNode(node.left.right);
|
|
9062
|
+
if (!lr)
|
|
9063
|
+
return false;
|
|
9064
|
+
const leftRight = evaluate(istate, lr);
|
|
9065
|
+
if (!hasValue(leftRight.value))
|
|
9066
|
+
return false;
|
|
9067
|
+
if (hasValue(right.value)) {
|
|
9068
|
+
// (ll + lr) + r => ll + (r + lr)
|
|
9069
|
+
// (ll - lr) - r => ll - (r + lr)
|
|
9070
|
+
// (ll + lr) - r => ll + (r - lr)
|
|
9071
|
+
// (ll - lr) + r => ll - (r - lr)
|
|
9072
|
+
const tmpNode = {
|
|
9073
|
+
type: "BinaryExpression",
|
|
9074
|
+
operator: node.operator === "+" || node.operator === "-"
|
|
9075
|
+
? node.operator === node.left.operator
|
|
9076
|
+
? "+"
|
|
9077
|
+
: "-"
|
|
9078
|
+
: node.operator,
|
|
9079
|
+
left: node.right,
|
|
9080
|
+
right: node.left.right,
|
|
9081
|
+
};
|
|
9082
|
+
if (tmpNode.operator === "+" || tmpNode.operator === "-") {
|
|
9083
|
+
if (leftRight.value.type & (32 /* TypeTag.Float */ | 64 /* TypeTag.Double */) ||
|
|
9084
|
+
right.value.type & (32 /* TypeTag.Float */ | 64 /* TypeTag.Double */)) {
|
|
9085
|
+
// we don't want to fold "a + 1.0 - 1.0" because
|
|
9086
|
+
// it could be there for rounding purposes
|
|
9087
|
+
const lsign = right.value.value < 0;
|
|
9088
|
+
const rsign = leftRight.value.value < 0 === (tmpNode.operator === "+");
|
|
9089
|
+
if (lsign !== rsign)
|
|
9090
|
+
return false;
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
const repType = evaluate(istate, tmpNode);
|
|
9094
|
+
if (!hasValue(repType.value))
|
|
9095
|
+
return false;
|
|
9096
|
+
const repNode = mcExprFromType(repType.value);
|
|
9097
|
+
if (!repNode)
|
|
9098
|
+
return false;
|
|
9099
|
+
left.node = node.left = node.left.left;
|
|
9100
|
+
node.right = repNode;
|
|
9101
|
+
istate.stack.splice(-1, 1, repType);
|
|
9102
|
+
repType.node = repNode;
|
|
9103
|
+
return true;
|
|
9104
|
+
}
|
|
9105
|
+
if (leftRight.value.type !== left.value.type ||
|
|
9106
|
+
leftRight.value.type !== right.value.type ||
|
|
9107
|
+
leftRight.value.type & (32 /* TypeTag.Float */ | 64 /* TypeTag.Double */)) {
|
|
9108
|
+
return false;
|
|
9109
|
+
}
|
|
9110
|
+
if (node.right.type === "BinaryExpression" &&
|
|
9111
|
+
(node.right.operator === node.operator ||
|
|
9112
|
+
((node.operator === "+" || node.operator === "-") &&
|
|
9113
|
+
(node.right.operator === "+" || node.right.operator === "-")))) {
|
|
9114
|
+
// (a + K1) + (b + K2) => (a + b) + (K1 + K2)
|
|
9115
|
+
const rr = getLiteralNode(node.right.right);
|
|
9116
|
+
if (!rr)
|
|
9117
|
+
return false;
|
|
9118
|
+
const rightRight = evaluate(istate, rr);
|
|
9119
|
+
if (!hasValue(rightRight.value))
|
|
9120
|
+
return false;
|
|
9121
|
+
const rightOp = node.operator === "+" || node.operator === "-"
|
|
9122
|
+
? ((node.left.operator === "+") === (node.right.operator === "+")) ===
|
|
9123
|
+
(node.operator === "+")
|
|
9124
|
+
? "+"
|
|
9125
|
+
: "-"
|
|
9126
|
+
: node.operator;
|
|
9127
|
+
const topOp = node.left.operator;
|
|
9128
|
+
const leftOp = node.operator;
|
|
9129
|
+
const rightType = evaluateBinaryTypes(rightOp, leftRight.value, rightRight.value);
|
|
9130
|
+
if (!hasValue(rightType))
|
|
9131
|
+
return false;
|
|
9132
|
+
const repNode = mcExprFromType(rightType);
|
|
9133
|
+
if (!repNode)
|
|
9134
|
+
return false;
|
|
9135
|
+
node.left.right = node.right.left;
|
|
9136
|
+
node.right = repNode;
|
|
9137
|
+
node.left.operator = leftOp;
|
|
9138
|
+
node.operator = topOp;
|
|
9139
|
+
istate.stack.splice(-1, 1, {
|
|
9140
|
+
value: rightType,
|
|
9141
|
+
node: repNode,
|
|
9142
|
+
embeddedEffects: false,
|
|
9143
|
+
});
|
|
9144
|
+
return true;
|
|
9145
|
+
}
|
|
9146
|
+
const op = node.operator;
|
|
9147
|
+
node.operator = node.left.operator;
|
|
9148
|
+
node.left.operator = op;
|
|
9149
|
+
leftRight.node = node.left.right;
|
|
9150
|
+
node.left.right = node.right;
|
|
9151
|
+
node.right = leftRight.node;
|
|
9152
|
+
istate.stack.splice(-1, 1, leftRight);
|
|
9153
|
+
return true;
|
|
9154
|
+
}
|
|
9155
|
+
function tryDeEnumerate(istate, node, elem) {
|
|
9156
|
+
if (node.type === "BinaryExpression" &&
|
|
9157
|
+
node.operator === "as" &&
|
|
9158
|
+
node.right.ts.length === 1 &&
|
|
9159
|
+
typeof node.right.ts[0] === "string") {
|
|
9160
|
+
elem += istate.stack.length;
|
|
9161
|
+
const item = istate.stack[elem];
|
|
9162
|
+
istate.stack[elem] = {
|
|
9163
|
+
value: deEnumerate(item.value),
|
|
9164
|
+
embeddedEffects: item.embeddedEffects,
|
|
9165
|
+
node: node.left,
|
|
9166
|
+
};
|
|
9167
|
+
return node.left;
|
|
9168
|
+
}
|
|
9169
|
+
return null;
|
|
6513
9170
|
}
|
|
6514
9171
|
|
|
6515
9172
|
|
|
@@ -6755,7 +9412,7 @@ function printBlockTrailer(block) {
|
|
|
6755
9412
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
6756
9413
|
/* harmony export */ "iX": () => (/* binding */ getStateNodeDeclsFromType)
|
|
6757
9414
|
/* harmony export */ });
|
|
6758
|
-
/* unused harmony exports typeTagName, LastTypeTag,
|
|
9415
|
+
/* unused harmony exports typeTagName, LastTypeTag, SingletonTypeTagsConst, UnionDataTypeTagsConst, ValueTypeTagsConst, ObjectLikeTagsConst, EnumTagsConst, isExact, isUnion, isSingleton, hasValue, hasNoData, cloneType, typeFromTypeStateNode, typeFromTypeStateNodes, typeFromSingleTypeSpec, typeFromTypespec, typeFromLiteral, mcExprFromType, castType, TruthyTypes, mustBeTrue, mustBeFalse, display, hasUnionData, getObjectValue, forEachUnionComponent, getUnionComponent, setUnionComponent */
|
|
6759
9416
|
/* harmony import */ var _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9642);
|
|
6760
9417
|
/* harmony import */ var _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0__);
|
|
6761
9418
|
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8180);
|
|
@@ -6830,7 +9487,7 @@ function typeTagName(tag) {
|
|
|
6830
9487
|
}
|
|
6831
9488
|
}
|
|
6832
9489
|
const LastTypeTag = 262144 /* TypeTag.Typedef */;
|
|
6833
|
-
const
|
|
9490
|
+
const SingletonTypeTagsConst = 1 /* TypeTag.Null */ | 2 /* TypeTag.False */ | 4 /* TypeTag.True */;
|
|
6834
9491
|
const UnionDataTypeTagsConst = 512 /* TypeTag.Array */ |
|
|
6835
9492
|
1024 /* TypeTag.Dictionary */ |
|
|
6836
9493
|
2048 /* TypeTag.Method */ |
|
|
@@ -6853,7 +9510,7 @@ const ObjectLikeTagsConst = 6 /* TypeTag.Boolean */ |
|
|
|
6853
9510
|
1024 /* TypeTag.Dictionary */ |
|
|
6854
9511
|
2048 /* TypeTag.Method */ |
|
|
6855
9512
|
65536 /* TypeTag.Enum */;
|
|
6856
|
-
const EnumTagsConst =
|
|
9513
|
+
const EnumTagsConst = SingletonTypeTagsConst | (ValueTypeTagsConst & ~131072 /* TypeTag.Symbol */);
|
|
6857
9514
|
function isExact(v) {
|
|
6858
9515
|
// check that there is exactly one bit set
|
|
6859
9516
|
return v.type !== 0 && !(v.type & (v.type - 1));
|
|
@@ -6862,27 +9519,18 @@ function isUnion(v) {
|
|
|
6862
9519
|
return v.type !== 0 && !isExact(v);
|
|
6863
9520
|
}
|
|
6864
9521
|
function isSingleton(v) {
|
|
6865
|
-
return isExact(v) && (v.type &
|
|
9522
|
+
return isExact(v) && (v.type & SingletonTypeTagsConst) !== 0;
|
|
6866
9523
|
}
|
|
6867
9524
|
function hasValue(v) {
|
|
6868
9525
|
return (isExact(v) &&
|
|
6869
|
-
((v.type &
|
|
9526
|
+
((v.type & SingletonTypeTagsConst) !== 0 || v.value !== undefined));
|
|
6870
9527
|
}
|
|
6871
9528
|
function hasNoData(v, t) {
|
|
6872
9529
|
if (v.value == null)
|
|
6873
9530
|
return true;
|
|
6874
9531
|
return ((hasUnionData(v.type)
|
|
6875
9532
|
? v.value.mask & t
|
|
6876
|
-
: v.type & t & ~
|
|
6877
|
-
}
|
|
6878
|
-
function lookupByFullName(state, fullName) {
|
|
6879
|
-
return fullName.split(".").reduce((results, part) => {
|
|
6880
|
-
return results
|
|
6881
|
-
.flatMap((result) => (0,_api__WEBPACK_IMPORTED_MODULE_2__.isStateNode)(result)
|
|
6882
|
-
? result.decls?.[part] || result.type_decls?.[part]
|
|
6883
|
-
: null)
|
|
6884
|
-
.filter((sn) => !!sn);
|
|
6885
|
-
}, [state.stack[0]]);
|
|
9533
|
+
: v.type & t & ~SingletonTypeTagsConst) === 0);
|
|
6886
9534
|
}
|
|
6887
9535
|
function cloneType(t) {
|
|
6888
9536
|
return { ...t };
|
|
@@ -7451,7 +10099,7 @@ function getObjectValue(t) {
|
|
|
7451
10099
|
}
|
|
7452
10100
|
function forEachUnionComponent(v, bits, fn) {
|
|
7453
10101
|
// never iterate the singleton bits, because they don't have data
|
|
7454
|
-
bits &= ~
|
|
10102
|
+
bits &= ~SingletonTypeTagsConst;
|
|
7455
10103
|
if (!bits)
|
|
7456
10104
|
return;
|
|
7457
10105
|
if ((v.type | bits) & UnionDataTypeTagsConst) {
|
|
@@ -7481,7 +10129,7 @@ function forEachUnionComponent(v, bits, fn) {
|
|
|
7481
10129
|
function getUnionComponent(v, tag) {
|
|
7482
10130
|
if (v.value == null)
|
|
7483
10131
|
return null;
|
|
7484
|
-
let bits = v.type & ~
|
|
10132
|
+
let bits = v.type & ~SingletonTypeTagsConst;
|
|
7485
10133
|
if (!bits)
|
|
7486
10134
|
return null;
|
|
7487
10135
|
if (bits & (bits - 1)) {
|
|
@@ -7555,7 +10203,7 @@ function getStateNodeDeclsFromType(state, object) {
|
|
|
7555
10203
|
next &= ~6 /* TypeTag.Boolean */;
|
|
7556
10204
|
}
|
|
7557
10205
|
const name = `Toybox.Lang.${typeTagName(bit)}`;
|
|
7558
|
-
const sns = lookupByFullName(state, name);
|
|
10206
|
+
const sns = (0,_api__WEBPACK_IMPORTED_MODULE_2__.lookupByFullName)(state, name);
|
|
7559
10207
|
sns.forEach((sn) => (0,_api__WEBPACK_IMPORTED_MODULE_2__.isStateNode)(sn) && decls.push(sn));
|
|
7560
10208
|
bits = next;
|
|
7561
10209
|
} while (bits);
|
|
@@ -7596,7 +10244,7 @@ function unionInto(to, from) {
|
|
|
7596
10244
|
return true;
|
|
7597
10245
|
}
|
|
7598
10246
|
const newTags = to.type | from.type;
|
|
7599
|
-
if (!(from.type & ~
|
|
10247
|
+
if (!(from.type & ~SingletonTypeTagsConst)) {
|
|
7600
10248
|
// - Adding singletons never affects the data.
|
|
7601
10249
|
if (newTags === to.type)
|
|
7602
10250
|
return false;
|
|
@@ -7858,7 +10506,7 @@ function clearValuesUnder(v, tag, clearTag = false) {
|
|
|
7858
10506
|
const newTag = clearTag ? v.type & ~tag : v.type | tag;
|
|
7859
10507
|
// If the incoming type consists of singletons,
|
|
7860
10508
|
// we can always merge it without affecting our data.
|
|
7861
|
-
tag &= ~
|
|
10509
|
+
tag &= ~SingletonTypeTagsConst;
|
|
7862
10510
|
if (!tag || v.value == null) {
|
|
7863
10511
|
v.type = newTag;
|
|
7864
10512
|
return;
|
|
@@ -7979,6 +10627,193 @@ function widenType(t) {
|
|
|
7979
10627
|
}
|
|
7980
10628
|
|
|
7981
10629
|
|
|
10630
|
+
/***/ }),
|
|
10631
|
+
|
|
10632
|
+
/***/ 424:
|
|
10633
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
10634
|
+
|
|
10635
|
+
"use strict";
|
|
10636
|
+
/* unused harmony export cleanupUnusedVars */
|
|
10637
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
|
|
10638
|
+
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
10639
|
+
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
10640
|
+
/* harmony import */ var _inliner__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(333);
|
|
10641
|
+
|
|
10642
|
+
|
|
10643
|
+
|
|
10644
|
+
function cleanupUnusedVars(state, node) {
|
|
10645
|
+
const parent = state.stack.slice(-1).pop().sn;
|
|
10646
|
+
if (parent.node !== node) {
|
|
10647
|
+
return false;
|
|
10648
|
+
}
|
|
10649
|
+
if (parent.type !== "BlockStatement") {
|
|
10650
|
+
throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
|
|
10651
|
+
}
|
|
10652
|
+
if (!parent.decls)
|
|
10653
|
+
return false;
|
|
10654
|
+
let toRemove = null;
|
|
10655
|
+
Object.values(parent.decls).forEach((decls) => {
|
|
10656
|
+
if (decls.length === 1 &&
|
|
10657
|
+
decls[0].type === "VariableDeclarator" &&
|
|
10658
|
+
!decls[0].used) {
|
|
10659
|
+
if (!toRemove)
|
|
10660
|
+
toRemove = {};
|
|
10661
|
+
toRemove[decls[0].name] = decls[0];
|
|
10662
|
+
}
|
|
10663
|
+
});
|
|
10664
|
+
if (!toRemove)
|
|
10665
|
+
return false;
|
|
10666
|
+
const varDeclarations = new Map();
|
|
10667
|
+
const stack = [];
|
|
10668
|
+
let changes = false;
|
|
10669
|
+
traverseAst(node, (node) => {
|
|
10670
|
+
switch (node.type) {
|
|
10671
|
+
case "SwitchCase":
|
|
10672
|
+
stack.push(node.consequent);
|
|
10673
|
+
break;
|
|
10674
|
+
case "BlockStatement":
|
|
10675
|
+
stack.push(node.body);
|
|
10676
|
+
break;
|
|
10677
|
+
}
|
|
10678
|
+
}, (node) => {
|
|
10679
|
+
switch (node.type) {
|
|
10680
|
+
case "SwitchCase":
|
|
10681
|
+
case "BlockStatement":
|
|
10682
|
+
stack.pop();
|
|
10683
|
+
break;
|
|
10684
|
+
case "VariableDeclaration": {
|
|
10685
|
+
node.declarations.forEach((decl, i) => {
|
|
10686
|
+
const name = variableDeclarationName(decl.id);
|
|
10687
|
+
if (hasProperty(toRemove, name)) {
|
|
10688
|
+
const info = varDeclarations.get(node);
|
|
10689
|
+
if (info) {
|
|
10690
|
+
info.indices.push(i);
|
|
10691
|
+
}
|
|
10692
|
+
else {
|
|
10693
|
+
varDeclarations.set(node, {
|
|
10694
|
+
parent: stack[stack.length - 1],
|
|
10695
|
+
indices: [i],
|
|
10696
|
+
});
|
|
10697
|
+
}
|
|
10698
|
+
}
|
|
10699
|
+
});
|
|
10700
|
+
break;
|
|
10701
|
+
}
|
|
10702
|
+
case "ExpressionStatement":
|
|
10703
|
+
if (node.expression.type === "AssignmentExpression") {
|
|
10704
|
+
if (node.expression.left.type === "Identifier" &&
|
|
10705
|
+
hasProperty(toRemove, node.expression.left.name)) {
|
|
10706
|
+
changes = true;
|
|
10707
|
+
return unused(state, node.expression.right);
|
|
10708
|
+
}
|
|
10709
|
+
}
|
|
10710
|
+
else if (node.expression.type === "UpdateExpression" &&
|
|
10711
|
+
node.expression.argument.type === "Identifier" &&
|
|
10712
|
+
hasProperty(toRemove, node.expression.argument.name)) {
|
|
10713
|
+
return false;
|
|
10714
|
+
}
|
|
10715
|
+
break;
|
|
10716
|
+
case "SequenceExpression": {
|
|
10717
|
+
for (let i = node.expressions.length; i--;) {
|
|
10718
|
+
const expr = node.expressions[i];
|
|
10719
|
+
if (expr.type === "AssignmentExpression") {
|
|
10720
|
+
if (expr.left.type === "Identifier" &&
|
|
10721
|
+
hasProperty(toRemove, expr.left.name)) {
|
|
10722
|
+
const rep = unused(state, expr.right);
|
|
10723
|
+
if (!rep.length) {
|
|
10724
|
+
changes = true;
|
|
10725
|
+
node.expressions.splice(i, 1);
|
|
10726
|
+
}
|
|
10727
|
+
else {
|
|
10728
|
+
// Sequence expressions can only be assignments
|
|
10729
|
+
// or update expressions. Even calls aren't allowed
|
|
10730
|
+
toRemove[expr.left.name] = null;
|
|
10731
|
+
expr.operator = "=";
|
|
10732
|
+
}
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
else if (expr.type === "UpdateExpression" &&
|
|
10736
|
+
expr.argument.type === "Identifier" &&
|
|
10737
|
+
hasProperty(toRemove, expr.argument.name)) {
|
|
10738
|
+
changes = true;
|
|
10739
|
+
node.expressions.splice(i, 1);
|
|
10740
|
+
}
|
|
10741
|
+
}
|
|
10742
|
+
break;
|
|
10743
|
+
}
|
|
10744
|
+
}
|
|
10745
|
+
return null;
|
|
10746
|
+
});
|
|
10747
|
+
varDeclarations.forEach((info, decl) => {
|
|
10748
|
+
let index = -1;
|
|
10749
|
+
for (let ii = info.indices.length, j = decl.declarations.length; ii--;) {
|
|
10750
|
+
const i = info.indices[ii];
|
|
10751
|
+
const vdecl = decl.declarations[i];
|
|
10752
|
+
const name = variableDeclarationName(vdecl.id);
|
|
10753
|
+
if (hasProperty(toRemove, name)) {
|
|
10754
|
+
const rep = vdecl.init ? unused(state, vdecl.init) : [];
|
|
10755
|
+
if (rep.length) {
|
|
10756
|
+
if ((state.sdkVersion || 0) < 4001007 &&
|
|
10757
|
+
rep.find((s) => s.type === "ExpressionStatement" &&
|
|
10758
|
+
(s.expression.type === "NewExpression" ||
|
|
10759
|
+
(s.expression.type === "MemberExpression" &&
|
|
10760
|
+
!s.expression.computed &&
|
|
10761
|
+
s.expression.object.type === "NewExpression")))) {
|
|
10762
|
+
// prior to 4.1.7 vanilla new expressions were discarded,
|
|
10763
|
+
// so don't create top level new expressions.
|
|
10764
|
+
continue;
|
|
10765
|
+
}
|
|
10766
|
+
if (parent.node.type === "ForStatement") {
|
|
10767
|
+
// declarations whose inits have side effects
|
|
10768
|
+
// can't be deleted from for statements.
|
|
10769
|
+
continue;
|
|
10770
|
+
}
|
|
10771
|
+
changes = true;
|
|
10772
|
+
if (index < 0) {
|
|
10773
|
+
index = info.parent.findIndex((s) => s === decl);
|
|
10774
|
+
if (index < 0) {
|
|
10775
|
+
throw new Error(`Failed to find variable declaration for ${variableDeclarationName(vdecl.id)}`);
|
|
10776
|
+
}
|
|
10777
|
+
}
|
|
10778
|
+
if (j > i + 1) {
|
|
10779
|
+
const tail = {
|
|
10780
|
+
...decl,
|
|
10781
|
+
declarations: decl.declarations.slice(i + 1, j),
|
|
10782
|
+
};
|
|
10783
|
+
if (decl.loc && vdecl.loc) {
|
|
10784
|
+
tail.loc = { ...decl.loc, start: vdecl.loc.end };
|
|
10785
|
+
tail.start = vdecl.end;
|
|
10786
|
+
}
|
|
10787
|
+
rep.push(tail);
|
|
10788
|
+
}
|
|
10789
|
+
if (decl.loc && vdecl.loc) {
|
|
10790
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
10791
|
+
decl.end = vdecl.start;
|
|
10792
|
+
}
|
|
10793
|
+
decl.declarations.splice(i);
|
|
10794
|
+
info.parent.splice(index + 1, 0, ...rep);
|
|
10795
|
+
j = i;
|
|
10796
|
+
continue;
|
|
10797
|
+
}
|
|
10798
|
+
if (toRemove[name]) {
|
|
10799
|
+
changes = true;
|
|
10800
|
+
j--;
|
|
10801
|
+
decl.declarations.splice(i, 1);
|
|
10802
|
+
if (i === j && decl.loc && vdecl.loc) {
|
|
10803
|
+
decl.loc = { ...decl.loc, end: vdecl.loc.start };
|
|
10804
|
+
decl.end = vdecl.start;
|
|
10805
|
+
}
|
|
10806
|
+
}
|
|
10807
|
+
else {
|
|
10808
|
+
delete vdecl.init;
|
|
10809
|
+
}
|
|
10810
|
+
}
|
|
10811
|
+
}
|
|
10812
|
+
});
|
|
10813
|
+
return changes;
|
|
10814
|
+
}
|
|
10815
|
+
|
|
10816
|
+
|
|
7982
10817
|
/***/ }),
|
|
7983
10818
|
|
|
7984
10819
|
/***/ 4405:
|
|
@@ -8028,7 +10863,7 @@ function renameVariable(state, locals, declName) {
|
|
|
8028
10863
|
let ok = false;
|
|
8029
10864
|
let i;
|
|
8030
10865
|
for (i = state.stack.length; i--;) {
|
|
8031
|
-
const elm = state.stack[i];
|
|
10866
|
+
const elm = state.stack[i].sn;
|
|
8032
10867
|
if (ok) {
|
|
8033
10868
|
if (hasProperty(elm.decls, name)) {
|
|
8034
10869
|
break;
|
|
@@ -8163,10 +10998,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
8163
10998
|
__webpack_require__.d(__webpack_exports__, {
|
|
8164
10999
|
"checkCompilerVersion": () => (/* binding */ checkCompilerVersion),
|
|
8165
11000
|
"collectNamespaces": () => (/* binding */ collectNamespaces),
|
|
11001
|
+
"createDocumentationMap": () => (/* binding */ createDocumentationMap),
|
|
8166
11002
|
"diagnostic": () => (/* binding */ diagnostic),
|
|
8167
11003
|
"diagnosticHelper": () => (/* binding */ diagnosticHelper),
|
|
11004
|
+
"findNamesInScope": () => (/* binding */ findNamesInScope),
|
|
8168
11005
|
"findUsingForNode": () => (/* binding */ findUsingForNode),
|
|
8169
11006
|
"formatAst": () => (/* binding */ formatAst),
|
|
11007
|
+
"formatAstLongLines": () => (/* binding */ formatAstLongLines),
|
|
8170
11008
|
"getApiFunctionInfo": () => (/* binding */ getApiFunctionInfo),
|
|
8171
11009
|
"getApiMapping": () => (/* binding */ getApiMapping),
|
|
8172
11010
|
"getSuperClasses": () => (/* binding */ getSuperClasses),
|
|
@@ -8174,7 +11012,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
8174
11012
|
"isLocal": () => (/* binding */ isLocal),
|
|
8175
11013
|
"isLookupCandidate": () => (/* binding */ isLookupCandidate),
|
|
8176
11014
|
"isStateNode": () => (/* binding */ isStateNode),
|
|
11015
|
+
"lookupByFullName": () => (/* binding */ lookupByFullName),
|
|
8177
11016
|
"lookupNext": () => (/* binding */ lookupNext),
|
|
11017
|
+
"lookupResultContains": () => (/* binding */ lookupResultContains),
|
|
11018
|
+
"lookupWithType": () => (/* binding */ lookupWithType),
|
|
11019
|
+
"makeToyboxLink": () => (/* binding */ makeToyboxLink),
|
|
11020
|
+
"mapVarDeclsByType": () => (/* binding */ mapVarDeclsByType),
|
|
8178
11021
|
"markInvokeClassMethod": () => (/* binding */ markInvokeClassMethod),
|
|
8179
11022
|
"parseSdkVersion": () => (/* binding */ parseSdkVersion),
|
|
8180
11023
|
"sameLookupResult": () => (/* binding */ sameLookupResult),
|
|
@@ -8194,6 +11037,8 @@ var promises_ = __webpack_require__(560);
|
|
|
8194
11037
|
const external_prettier_namespaceObject = require("prettier");
|
|
8195
11038
|
// EXTERNAL MODULE: ./src/ast.ts
|
|
8196
11039
|
var src_ast = __webpack_require__(6652);
|
|
11040
|
+
// EXTERNAL MODULE: ./src/mc-rewrite.ts
|
|
11041
|
+
var mc_rewrite = __webpack_require__(5530);
|
|
8197
11042
|
// EXTERNAL MODULE: ./src/negative-fixups.ts
|
|
8198
11043
|
var negative_fixups = __webpack_require__(4499);
|
|
8199
11044
|
// EXTERNAL MODULE: ./src/optimizer-types.ts
|
|
@@ -8717,13 +11562,14 @@ function add_one_resource(state, doc, module, e) {
|
|
|
8717
11562
|
}
|
|
8718
11563
|
}
|
|
8719
11564
|
|
|
8720
|
-
// EXTERNAL MODULE: external "./util.cjs"
|
|
8721
|
-
var external_util_cjs_ = __webpack_require__(6906);
|
|
8722
11565
|
// EXTERNAL MODULE: ./src/type-flow.ts
|
|
8723
11566
|
var type_flow = __webpack_require__(4859);
|
|
11567
|
+
// EXTERNAL MODULE: ./src/type-flow/types.ts
|
|
11568
|
+
var types = __webpack_require__(7255);
|
|
11569
|
+
// EXTERNAL MODULE: external "./util.cjs"
|
|
11570
|
+
var external_util_cjs_ = __webpack_require__(6906);
|
|
8724
11571
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
8725
11572
|
|
|
8726
|
-
|
|
8727
11573
|
function visitorNode(node) {
|
|
8728
11574
|
if (node.type === "Identifier") {
|
|
8729
11575
|
return node;
|
|
@@ -8742,28 +11588,11 @@ function visitorNode(node) {
|
|
|
8742
11588
|
}
|
|
8743
11589
|
return node;
|
|
8744
11590
|
}
|
|
8745
|
-
function visitReferences(state, ast, name, defn, callback, includeDefs = false, filter = null, typeMap = null) {
|
|
8746
|
-
const lookup = (node, nonLocal = false) =>
|
|
8747
|
-
const results = nonLocal ? state.lookupNonlocal(node) : state.lookup(node);
|
|
8748
|
-
if (results[1] || !typeMap)
|
|
8749
|
-
return results;
|
|
8750
|
-
if (node.type === "MemberExpression" && !node.computed) {
|
|
8751
|
-
const objectType = typeMap.get(node.object);
|
|
8752
|
-
if (!objectType)
|
|
8753
|
-
return results;
|
|
8754
|
-
const [, decls] = (0,type_flow/* findObjectDeclsByProperty */.nK)(state, objectType, node);
|
|
8755
|
-
if (decls) {
|
|
8756
|
-
const next = (0,external_api_cjs_.lookupNext)(state, [{ parent: null, results: decls }], "decls", node.property);
|
|
8757
|
-
if (next) {
|
|
8758
|
-
return [node.property.name, next];
|
|
8759
|
-
}
|
|
8760
|
-
}
|
|
8761
|
-
}
|
|
8762
|
-
return results;
|
|
8763
|
-
};
|
|
11591
|
+
function visitReferences(state, ast, name, defn, callback, includeDefs = false, filter = null, typeMap = null, findSingleDefinition = false) {
|
|
11592
|
+
const lookup = (node, nonLocal = false) => (0,external_api_cjs_.lookupWithType)(state, node, typeMap, nonLocal);
|
|
8764
11593
|
const checkResults = ([name, results], node) => {
|
|
8765
11594
|
if (name && results) {
|
|
8766
|
-
if (!defn || (0,external_api_cjs_.
|
|
11595
|
+
if (!defn || (0,external_api_cjs_.lookupResultContains)(results, defn)) {
|
|
8767
11596
|
if (callback(node, results, false) === false) {
|
|
8768
11597
|
return [];
|
|
8769
11598
|
}
|
|
@@ -8776,6 +11605,35 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8776
11605
|
}
|
|
8777
11606
|
return null;
|
|
8778
11607
|
};
|
|
11608
|
+
const visitDef = (node, def, decls = "decls") => {
|
|
11609
|
+
const id = node.type === "BinaryExpression" ? node.left : node;
|
|
11610
|
+
if (filter && !filter(id))
|
|
11611
|
+
return;
|
|
11612
|
+
let [scope, parent] = state.stack.slice(-2).reverse();
|
|
11613
|
+
let results;
|
|
11614
|
+
if (def) {
|
|
11615
|
+
if (scope.sn.node !== def) {
|
|
11616
|
+
parent = scope;
|
|
11617
|
+
}
|
|
11618
|
+
results = parent.sn[decls]?.[id.name];
|
|
11619
|
+
if (!results)
|
|
11620
|
+
return;
|
|
11621
|
+
const thisDefn = results.find((decl) => decl.node === def);
|
|
11622
|
+
if (!thisDefn) {
|
|
11623
|
+
return;
|
|
11624
|
+
}
|
|
11625
|
+
if (findSingleDefinition) {
|
|
11626
|
+
results = [thisDefn];
|
|
11627
|
+
}
|
|
11628
|
+
}
|
|
11629
|
+
else {
|
|
11630
|
+
if (!parent) {
|
|
11631
|
+
return;
|
|
11632
|
+
}
|
|
11633
|
+
results = [scope.sn];
|
|
11634
|
+
}
|
|
11635
|
+
checkResults([id.name, [{ parent: parent.sn, results }]], id);
|
|
11636
|
+
};
|
|
8779
11637
|
state.pre = (node) => {
|
|
8780
11638
|
if (filter && !filter(node))
|
|
8781
11639
|
return [];
|
|
@@ -8812,7 +11670,8 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8812
11670
|
// it will be ignored, and the lookup will start as if the
|
|
8813
11671
|
// call had been written self.foo() rather than foo().
|
|
8814
11672
|
if (node.callee.type === "Identifier") {
|
|
8815
|
-
if (!name || node.callee.name === name)
|
|
11673
|
+
if ((!name || node.callee.name === name) &&
|
|
11674
|
+
(!filter || filter(node.callee))) {
|
|
8816
11675
|
/* ignore return value */
|
|
8817
11676
|
checkResults(lookup(node.callee, true), node.callee);
|
|
8818
11677
|
}
|
|
@@ -8848,29 +11707,47 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8848
11707
|
return ["returnType"];
|
|
8849
11708
|
}
|
|
8850
11709
|
case "ModuleDeclaration":
|
|
8851
|
-
if (includeDefs)
|
|
8852
|
-
|
|
11710
|
+
if (includeDefs) {
|
|
11711
|
+
visitDef(node.id);
|
|
11712
|
+
}
|
|
8853
11713
|
return ["body"];
|
|
8854
11714
|
case "ClassDeclaration":
|
|
8855
|
-
if (includeDefs)
|
|
8856
|
-
|
|
11715
|
+
if (includeDefs) {
|
|
11716
|
+
visitDef(node.id, node);
|
|
11717
|
+
}
|
|
8857
11718
|
return ["body", "superClass"];
|
|
8858
11719
|
case "FunctionDeclaration":
|
|
8859
|
-
if (includeDefs)
|
|
8860
|
-
|
|
11720
|
+
if (includeDefs) {
|
|
11721
|
+
visitDef(node.id, node);
|
|
11722
|
+
}
|
|
8861
11723
|
return ["params", "returnType", "body"];
|
|
8862
11724
|
case "TypedefDeclaration":
|
|
8863
|
-
if (includeDefs)
|
|
8864
|
-
|
|
11725
|
+
if (includeDefs) {
|
|
11726
|
+
visitDef(node.id, node, "type_decls");
|
|
11727
|
+
}
|
|
8865
11728
|
return ["ts"];
|
|
8866
11729
|
case "VariableDeclarator":
|
|
8867
|
-
if (includeDefs)
|
|
8868
|
-
|
|
11730
|
+
if (includeDefs) {
|
|
11731
|
+
visitDef(node.id, node);
|
|
11732
|
+
}
|
|
11733
|
+
if (node.id.type === "BinaryExpression") {
|
|
11734
|
+
state.traverse(node.id.right);
|
|
11735
|
+
}
|
|
8869
11736
|
return ["init"];
|
|
8870
11737
|
case "EnumDeclaration":
|
|
8871
|
-
if (includeDefs)
|
|
11738
|
+
if (includeDefs) {
|
|
11739
|
+
if (node.id) {
|
|
11740
|
+
visitDef(node.id, node, "type_decls");
|
|
11741
|
+
}
|
|
8872
11742
|
break;
|
|
11743
|
+
}
|
|
8873
11744
|
return [];
|
|
11745
|
+
case "EnumStringMember": {
|
|
11746
|
+
if (!filter || filter(node.id)) {
|
|
11747
|
+
checkResults([node.id.name, [{ parent: state.top().sn, results: [node] }]], node);
|
|
11748
|
+
}
|
|
11749
|
+
break;
|
|
11750
|
+
}
|
|
8874
11751
|
case "CatchClause":
|
|
8875
11752
|
if (includeDefs)
|
|
8876
11753
|
break;
|
|
@@ -8897,6 +11774,9 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8897
11774
|
|
|
8898
11775
|
|
|
8899
11776
|
|
|
11777
|
+
|
|
11778
|
+
|
|
11779
|
+
|
|
8900
11780
|
/*
|
|
8901
11781
|
* This is an unfortunate hack. I want to be able to extract things
|
|
8902
11782
|
* like the types of all of a Class's variables (in particular the type
|
|
@@ -9101,6 +11981,19 @@ function sameLookupDefinition(a, b) {
|
|
|
9101
11981
|
function sameLookupResult(a, b) {
|
|
9102
11982
|
return (0,external_util_cjs_.sameArrays)(a, b, sameLookupDefinition);
|
|
9103
11983
|
}
|
|
11984
|
+
function declKey(decl) {
|
|
11985
|
+
return isStateNode(decl)
|
|
11986
|
+
? decl.type === "ModuleDeclaration"
|
|
11987
|
+
? decl.fullName
|
|
11988
|
+
: decl.node
|
|
11989
|
+
: decl;
|
|
11990
|
+
}
|
|
11991
|
+
function lookupResultContains(a, b) {
|
|
11992
|
+
if (!b.length)
|
|
11993
|
+
return false;
|
|
11994
|
+
const bs = new Set(b.flatMap((bdef) => bdef.results.map(declKey)));
|
|
11995
|
+
return a.some((adef) => adef.results.some((adecl) => bs.has(declKey(adecl))));
|
|
11996
|
+
}
|
|
9104
11997
|
function isLookupCandidate(node) {
|
|
9105
11998
|
return node.computed
|
|
9106
11999
|
? node.property.type === "UnaryExpression" &&
|
|
@@ -9177,26 +12070,26 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
|
|
|
9177
12070
|
}
|
|
9178
12071
|
case "ThisExpression": {
|
|
9179
12072
|
for (let i = stack.length;;) {
|
|
9180
|
-
const si = stack[--i];
|
|
12073
|
+
const si = stack[--i].sn;
|
|
9181
12074
|
if (si.type === "ModuleDeclaration" ||
|
|
9182
12075
|
si.type === "ClassDeclaration" ||
|
|
9183
12076
|
!i) {
|
|
9184
12077
|
return [
|
|
9185
12078
|
name || si.name,
|
|
9186
|
-
[{ parent: i ? stack[i - 1] : null, results: [si] }],
|
|
12079
|
+
[{ parent: i ? stack[i - 1].sn : null, results: [si] }],
|
|
9187
12080
|
];
|
|
9188
12081
|
}
|
|
9189
12082
|
}
|
|
9190
12083
|
}
|
|
9191
12084
|
case "Identifier": {
|
|
9192
12085
|
if (node.name === "$") {
|
|
9193
|
-
return [name || node.name, [{ parent: null, results: [stack[0]] }]];
|
|
12086
|
+
return [name || node.name, [{ parent: null, results: [stack[0].sn] }]];
|
|
9194
12087
|
}
|
|
9195
12088
|
let inStatic = false;
|
|
9196
12089
|
let checkedImports = ignoreImports;
|
|
9197
12090
|
let imports = null;
|
|
9198
12091
|
for (let i = stack.length; i--;) {
|
|
9199
|
-
const si = stack[i];
|
|
12092
|
+
const si = stack[i].sn;
|
|
9200
12093
|
switch (si.type) {
|
|
9201
12094
|
case "ClassDeclaration":
|
|
9202
12095
|
if (inStatic && state.config?.enforceStatic !== "NO") {
|
|
@@ -9293,6 +12186,26 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
|
|
|
9293
12186
|
}
|
|
9294
12187
|
return [false, false];
|
|
9295
12188
|
}
|
|
12189
|
+
function lookupWithType(state, node, typeMap, nonLocal = false, stack = null) {
|
|
12190
|
+
const results = nonLocal
|
|
12191
|
+
? state.lookupNonlocal(node, null, stack)
|
|
12192
|
+
: state.lookup(node, null, stack);
|
|
12193
|
+
if (results[1] || !typeMap)
|
|
12194
|
+
return results;
|
|
12195
|
+
if (node.type === "MemberExpression" && !node.computed) {
|
|
12196
|
+
const objectType = typeMap.get(node.object);
|
|
12197
|
+
if (!objectType)
|
|
12198
|
+
return results;
|
|
12199
|
+
const [, decls] = (0,type_flow/* findObjectDeclsByProperty */.nK)(state, objectType, node);
|
|
12200
|
+
if (decls) {
|
|
12201
|
+
const next = lookupNext(state, [{ parent: null, results: decls }], "decls", node.property);
|
|
12202
|
+
if (next) {
|
|
12203
|
+
return [node.property.name, next];
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
12207
|
+
return results;
|
|
12208
|
+
}
|
|
9296
12209
|
function stateFuncs() {
|
|
9297
12210
|
let currentEnum = null;
|
|
9298
12211
|
return {
|
|
@@ -9330,9 +12243,10 @@ function stateFuncs() {
|
|
|
9330
12243
|
return lookup(this, "type_decls", node, name, stack);
|
|
9331
12244
|
},
|
|
9332
12245
|
stackClone() {
|
|
9333
|
-
return this.stack.
|
|
9334
|
-
|
|
9335
|
-
|
|
12246
|
+
return this.stack.slice();
|
|
12247
|
+
},
|
|
12248
|
+
top() {
|
|
12249
|
+
return this.stack[this.stack.length - 1];
|
|
9336
12250
|
},
|
|
9337
12251
|
traverse(root) {
|
|
9338
12252
|
return (0,src_ast/* traverseAst */.lA)(root, (node) => {
|
|
@@ -9359,7 +12273,7 @@ function stateFuncs() {
|
|
|
9359
12273
|
if (this.stack.length !== 1) {
|
|
9360
12274
|
throw new Error("Unexpected stack length for Program node");
|
|
9361
12275
|
}
|
|
9362
|
-
this.stack[0].node = node;
|
|
12276
|
+
this.stack[0].sn.node = node;
|
|
9363
12277
|
break;
|
|
9364
12278
|
case "TypeSpecList":
|
|
9365
12279
|
case "TypeSpecPart":
|
|
@@ -9367,10 +12281,9 @@ function stateFuncs() {
|
|
|
9367
12281
|
break;
|
|
9368
12282
|
case "ImportModule":
|
|
9369
12283
|
case "Using": {
|
|
9370
|
-
const
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
}
|
|
12284
|
+
const parent = { ...this.stack.pop() };
|
|
12285
|
+
this.stack.push(parent);
|
|
12286
|
+
parent.usings = parent.usings ? { ...parent.usings } : {};
|
|
9374
12287
|
const name = (node.type === "Using" && node.as && node.as.name) ||
|
|
9375
12288
|
(node.id.type === "Identifier"
|
|
9376
12289
|
? node.id.name
|
|
@@ -9382,6 +12295,7 @@ function stateFuncs() {
|
|
|
9382
12295
|
parent.imports = [using];
|
|
9383
12296
|
}
|
|
9384
12297
|
else {
|
|
12298
|
+
parent.imports = parent.imports.slice();
|
|
9385
12299
|
const index = parent.imports.findIndex((using) => (using.node.id.type === "Identifier"
|
|
9386
12300
|
? using.node.id.name
|
|
9387
12301
|
: using.node.id.property.name) === name);
|
|
@@ -9394,35 +12308,39 @@ function stateFuncs() {
|
|
|
9394
12308
|
}
|
|
9395
12309
|
case "CatchClause":
|
|
9396
12310
|
if (node.param) {
|
|
9397
|
-
const
|
|
12311
|
+
const parent = this.top().sn;
|
|
9398
12312
|
if (!parent.decls)
|
|
9399
12313
|
parent.decls = {};
|
|
9400
12314
|
const id = node.param.type === "Identifier"
|
|
9401
12315
|
? node.param
|
|
9402
12316
|
: node.param.left;
|
|
9403
12317
|
this.stack.push({
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
12318
|
+
sn: {
|
|
12319
|
+
type: "BlockStatement",
|
|
12320
|
+
fullName: parent.fullName,
|
|
12321
|
+
name: undefined,
|
|
12322
|
+
node: node.body,
|
|
12323
|
+
decls: { [id.name]: [id] },
|
|
12324
|
+
attributes: 0,
|
|
12325
|
+
},
|
|
9410
12326
|
});
|
|
9411
12327
|
}
|
|
9412
12328
|
break;
|
|
9413
12329
|
case "ForStatement":
|
|
9414
12330
|
if (node.init && node.init.type === "VariableDeclaration") {
|
|
9415
12331
|
this.stack.push({
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
12332
|
+
sn: {
|
|
12333
|
+
type: "BlockStatement",
|
|
12334
|
+
fullName: this.top().sn.fullName,
|
|
12335
|
+
name: undefined,
|
|
12336
|
+
node: node,
|
|
12337
|
+
attributes: 0,
|
|
12338
|
+
},
|
|
9421
12339
|
});
|
|
9422
12340
|
}
|
|
9423
12341
|
break;
|
|
9424
12342
|
case "BlockStatement": {
|
|
9425
|
-
const
|
|
12343
|
+
const parent = this.top().sn;
|
|
9426
12344
|
if (parent.node === node ||
|
|
9427
12345
|
(parent.type !== "FunctionDeclaration" &&
|
|
9428
12346
|
parent.type !== "BlockStatement")) {
|
|
@@ -9433,10 +12351,10 @@ function stateFuncs() {
|
|
|
9433
12351
|
case "ClassDeclaration":
|
|
9434
12352
|
case "FunctionDeclaration":
|
|
9435
12353
|
case "ModuleDeclaration": {
|
|
9436
|
-
const
|
|
12354
|
+
const parent = this.top().sn;
|
|
9437
12355
|
const name = "id" in node ? node.id && node.id.name : undefined;
|
|
9438
12356
|
const fullName = this.stack
|
|
9439
|
-
.map((e) => e.name)
|
|
12357
|
+
.map((e) => e.sn.name)
|
|
9440
12358
|
.concat(name)
|
|
9441
12359
|
.filter((e) => e != null)
|
|
9442
12360
|
.join(".");
|
|
@@ -9449,7 +12367,7 @@ function stateFuncs() {
|
|
|
9449
12367
|
? 0
|
|
9450
12368
|
: stateNodeAttrs(node.attrs),
|
|
9451
12369
|
};
|
|
9452
|
-
this.stack.push(elm);
|
|
12370
|
+
this.stack.push({ sn: elm });
|
|
9453
12371
|
if (name) {
|
|
9454
12372
|
if (!parent.decls)
|
|
9455
12373
|
parent.decls = {};
|
|
@@ -9458,7 +12376,7 @@ function stateFuncs() {
|
|
|
9458
12376
|
const e = parent.decls[name].find((d) => isStateNode(d) && d[what] === elm[what]);
|
|
9459
12377
|
if (e != null) {
|
|
9460
12378
|
e.node = node;
|
|
9461
|
-
this.
|
|
12379
|
+
this.top().sn = e;
|
|
9462
12380
|
break;
|
|
9463
12381
|
}
|
|
9464
12382
|
}
|
|
@@ -9492,7 +12410,7 @@ function stateFuncs() {
|
|
|
9492
12410
|
this.inType++;
|
|
9493
12411
|
break;
|
|
9494
12412
|
}
|
|
9495
|
-
const
|
|
12413
|
+
const parent = this.top().sn;
|
|
9496
12414
|
const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
|
|
9497
12415
|
node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
|
|
9498
12416
|
}
|
|
@@ -9500,7 +12418,7 @@ function stateFuncs() {
|
|
|
9500
12418
|
case "TypedefDeclaration": {
|
|
9501
12419
|
this.inType++;
|
|
9502
12420
|
const name = node.id.name;
|
|
9503
|
-
const
|
|
12421
|
+
const parent = this.top().sn;
|
|
9504
12422
|
if (!parent.type_decls)
|
|
9505
12423
|
parent.type_decls = {};
|
|
9506
12424
|
if (!(0,src_ast/* hasProperty */.l$)(parent.type_decls, name)) {
|
|
@@ -9524,7 +12442,7 @@ function stateFuncs() {
|
|
|
9524
12442
|
break;
|
|
9525
12443
|
}
|
|
9526
12444
|
case "VariableDeclaration": {
|
|
9527
|
-
const
|
|
12445
|
+
const parent = this.top().sn;
|
|
9528
12446
|
if (!parent.decls)
|
|
9529
12447
|
parent.decls = {};
|
|
9530
12448
|
const decls = parent.decls;
|
|
@@ -9560,7 +12478,7 @@ function stateFuncs() {
|
|
|
9560
12478
|
throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${this.inType}.`);
|
|
9561
12479
|
}
|
|
9562
12480
|
this.inType--;
|
|
9563
|
-
const
|
|
12481
|
+
const parent = this.top().sn;
|
|
9564
12482
|
const values = parent.decls || (parent.decls = {});
|
|
9565
12483
|
let prev = -1;
|
|
9566
12484
|
node.members.forEach((m, i) => {
|
|
@@ -9652,17 +12570,19 @@ function stateFuncs() {
|
|
|
9652
12570
|
this.inType++;
|
|
9653
12571
|
break;
|
|
9654
12572
|
}
|
|
9655
|
-
const
|
|
9656
|
-
if (parent.node === node ||
|
|
12573
|
+
const parent = this.top();
|
|
12574
|
+
if (parent.sn.node === node ||
|
|
9657
12575
|
// The pre function might cause node.body to be skipped,
|
|
9658
12576
|
// so we need to check here, just in case.
|
|
9659
12577
|
// (this actually happens with prettier-extenison-monkeyc's
|
|
9660
12578
|
// findItemsByRange)
|
|
9661
|
-
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
12579
|
+
(node.type === "CatchClause" && parent.sn.node === node.body)) {
|
|
12580
|
+
let top = this.stack.pop();
|
|
12581
|
+
if (node.type === "Program") {
|
|
12582
|
+
top = { ...top };
|
|
12583
|
+
delete top.usings;
|
|
12584
|
+
delete top.imports;
|
|
12585
|
+
this.stack.push(top);
|
|
9666
12586
|
}
|
|
9667
12587
|
}
|
|
9668
12588
|
}
|
|
@@ -9688,11 +12608,13 @@ function collectNamespaces(ast, stateIn) {
|
|
|
9688
12608
|
if (!state.stack) {
|
|
9689
12609
|
state.stack = [
|
|
9690
12610
|
{
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
12611
|
+
sn: {
|
|
12612
|
+
type: "Program",
|
|
12613
|
+
name: "$",
|
|
12614
|
+
fullName: "$",
|
|
12615
|
+
node: undefined,
|
|
12616
|
+
attributes: 0,
|
|
12617
|
+
},
|
|
9696
12618
|
},
|
|
9697
12619
|
];
|
|
9698
12620
|
}
|
|
@@ -9717,10 +12639,10 @@ function collectNamespaces(ast, stateIn) {
|
|
|
9717
12639
|
if (state.stack.length !== 1) {
|
|
9718
12640
|
throw new Error("Invalid AST!");
|
|
9719
12641
|
}
|
|
9720
|
-
if (state.stack[0].type !== "Program") {
|
|
12642
|
+
if (state.stack[0].sn.type !== "Program") {
|
|
9721
12643
|
throw new Error("Bottom of stack was not a Program!");
|
|
9722
12644
|
}
|
|
9723
|
-
return state.stack[0];
|
|
12645
|
+
return state.stack[0].sn;
|
|
9724
12646
|
}
|
|
9725
12647
|
function formatAst(node, monkeyCSource = null, options = null) {
|
|
9726
12648
|
/*
|
|
@@ -9759,10 +12681,152 @@ function formatAst(node, monkeyCSource = null, options = null) {
|
|
|
9759
12681
|
endOfLine: "lf",
|
|
9760
12682
|
});
|
|
9761
12683
|
}
|
|
12684
|
+
function findNamesInExactScope(decl, regexp) {
|
|
12685
|
+
if (!decl.decls)
|
|
12686
|
+
return [];
|
|
12687
|
+
return Object.entries(decl.decls).flatMap(([key, value]) => regexp.test(key) ? value : []);
|
|
12688
|
+
}
|
|
12689
|
+
function findNamesInScope(declStack, pattern) {
|
|
12690
|
+
const regex = typeof pattern === "string"
|
|
12691
|
+
? new RegExp(pattern.replace(/\W/g, "").split("").join(".*"), "i")
|
|
12692
|
+
: pattern;
|
|
12693
|
+
const results = new Map();
|
|
12694
|
+
const helper = (decls, depth) => {
|
|
12695
|
+
decls.forEach((parent) => {
|
|
12696
|
+
if (parent.type === "ClassDeclaration") {
|
|
12697
|
+
if (parent.superClass && parent.superClass !== true) {
|
|
12698
|
+
helper(parent.superClass, depth + 1);
|
|
12699
|
+
}
|
|
12700
|
+
}
|
|
12701
|
+
findNamesInExactScope(parent, regex).forEach((sn) => {
|
|
12702
|
+
results.set(sn, { parent, depth });
|
|
12703
|
+
});
|
|
12704
|
+
});
|
|
12705
|
+
};
|
|
12706
|
+
let depth = 0;
|
|
12707
|
+
while (depth < declStack.length) {
|
|
12708
|
+
helper(declStack[declStack.length - 1 - depth], depth);
|
|
12709
|
+
depth++;
|
|
12710
|
+
}
|
|
12711
|
+
return Array.from(results);
|
|
12712
|
+
}
|
|
12713
|
+
function mapVarDeclsByType(state, decls, node, typeMap) {
|
|
12714
|
+
return decls.flatMap((decl) => {
|
|
12715
|
+
if (decl.type === "VariableDeclarator" ||
|
|
12716
|
+
decl.type === "Identifier" ||
|
|
12717
|
+
decl.type === "BinaryExpression") {
|
|
12718
|
+
const type = typeMap?.get(node);
|
|
12719
|
+
return type ? (0,types/* getStateNodeDeclsFromType */.iX)(state, type) : [];
|
|
12720
|
+
}
|
|
12721
|
+
return decl;
|
|
12722
|
+
});
|
|
12723
|
+
}
|
|
12724
|
+
function formatAstLongLines(node) {
|
|
12725
|
+
return formatAst(node, null, { printWidth: 10000 });
|
|
12726
|
+
}
|
|
12727
|
+
async function createDocumentationMap(functionDocumentation) {
|
|
12728
|
+
const docMap = new Map();
|
|
12729
|
+
const state = await (0,mc_rewrite/* analyze */.BJ)({}, {}, undefined, {});
|
|
12730
|
+
functionDocumentation.forEach((info) => {
|
|
12731
|
+
state.allFunctions[info.name]?.forEach((decl) => decl.node?.loc?.source === "api.mir" &&
|
|
12732
|
+
decl.fullName.endsWith(`.${info.parent}.${info.name}`) &&
|
|
12733
|
+
docMap.set(decl.fullName, info.doc
|
|
12734
|
+
.replace(/(\*.*?)\s*<br\/>\s*(?!\s*\*)/g, "$1\n\n")
|
|
12735
|
+
.replace(/@example(.*?)(@|$)/g, (match, m1, m2) => `\n#### Example\n\`\`\`${m1.replace(/<br\/>/g, "\n")}\`\`\`${m2}`)
|
|
12736
|
+
.replace(/@note/g, "\n#### Note\n")
|
|
12737
|
+
.replace(/@see/, "\n#### See Also:\n$&")
|
|
12738
|
+
.replace(/@see\s+(.*?)(?=<br\/>)/g, "\n * {$1}")
|
|
12739
|
+
.replace(/@throws/, "\n#### Throws:\n$&")
|
|
12740
|
+
.replace(/@throws\s+(.*?)(?=<br\/>)/g, "\n * $1")
|
|
12741
|
+
.replace(/@since\s+(.*?)(?=<br\/>)/, "\n#### Since:\nAPI Level $1\n")
|
|
12742
|
+
.replace(/<div class="description">/, "### Description\n")
|
|
12743
|
+
.replace(/<div class="param">/, "\n#### Parameters\n$&")
|
|
12744
|
+
.replace(/<div class="param">/g, " \n*")
|
|
12745
|
+
.replace(/\s*<div[^>]*>/g, " \n\n")
|
|
12746
|
+
.replace(/\s*<br\/>\s*([@*])/g, "\n$1")
|
|
12747
|
+
//.replace(/\s\s\s(\s)*(?!\/\/)/g, " \n")
|
|
12748
|
+
.replace(/<[^>]*>/g, "")
|
|
12749
|
+
.replace(/@/g, " \n\n@")
|
|
12750
|
+
//.replace(/\s\*/g, " \n*")
|
|
12751
|
+
.replace(/\+(\w+)\+/g, "`$1`")
|
|
12752
|
+
.trim()
|
|
12753
|
+
.replace(/\[((\s*(\w+::)+\w+\s*,)*(\s*(\w+::)+\w+\s*))\]/g, (arg, a1) => {
|
|
12754
|
+
return a1
|
|
12755
|
+
.split(",")
|
|
12756
|
+
.map((s) => {
|
|
12757
|
+
const name = s.trim().replace(/::/g, ".");
|
|
12758
|
+
const decl = lookupByFullName(state, name);
|
|
12759
|
+
if (decl && decl.length === 1) {
|
|
12760
|
+
const sn = decl[0];
|
|
12761
|
+
const link = makeToyboxLink(sn);
|
|
12762
|
+
return `[${sn.name || name}](${link})`;
|
|
12763
|
+
}
|
|
12764
|
+
return s;
|
|
12765
|
+
})
|
|
12766
|
+
.join(" or ");
|
|
12767
|
+
})
|
|
12768
|
+
.replace(/\{(\s*(?:\w+::)+\w+(?:#\w+)?|https:\/\/\S+)\s*(\S.*?)?\s*\}/g, (arg, a1, a2) => {
|
|
12769
|
+
if (a1.startsWith("https://")) {
|
|
12770
|
+
return `[${a2 || a1}](${a1})`;
|
|
12771
|
+
}
|
|
12772
|
+
let name = a1.trim().replace(/::|#/g, ".");
|
|
12773
|
+
if (!name.startsWith("Toybox")) {
|
|
12774
|
+
name = `Toybox.${name}`;
|
|
12775
|
+
}
|
|
12776
|
+
const decl = lookupByFullName(state, name);
|
|
12777
|
+
if (decl && decl.length === 1) {
|
|
12778
|
+
const link = makeToyboxLink(decl[0]);
|
|
12779
|
+
return `[${a2 || a1}](${link})`;
|
|
12780
|
+
}
|
|
12781
|
+
return arg;
|
|
12782
|
+
})));
|
|
12783
|
+
});
|
|
12784
|
+
return docMap;
|
|
12785
|
+
}
|
|
12786
|
+
function makeToyboxLink(result) {
|
|
12787
|
+
const make_link = (fullName, fragment) => {
|
|
12788
|
+
const path = fullName.split(".");
|
|
12789
|
+
return (`https://developer.garmin.com/connect-iq/api-docs/${path
|
|
12790
|
+
.slice(1, fragment ? -1 : undefined)
|
|
12791
|
+
.join("/")}.html` +
|
|
12792
|
+
(fragment ? `#${path.slice(-1)[0]}-${fragment}` : ""));
|
|
12793
|
+
};
|
|
12794
|
+
switch (result.type) {
|
|
12795
|
+
case "ClassDeclaration":
|
|
12796
|
+
case "ModuleDeclaration":
|
|
12797
|
+
if (result.fullName.startsWith("$.Toybox")) {
|
|
12798
|
+
return make_link(result.fullName);
|
|
12799
|
+
}
|
|
12800
|
+
break;
|
|
12801
|
+
case "FunctionDeclaration":
|
|
12802
|
+
return make_link(result.fullName, "instance_function");
|
|
12803
|
+
case "EnumStringMember":
|
|
12804
|
+
if (result.init.enumType && typeof result.init.enumType === "string") {
|
|
12805
|
+
return make_link("$." + result.init.enumType, "module");
|
|
12806
|
+
}
|
|
12807
|
+
break;
|
|
12808
|
+
case "EnumDeclaration":
|
|
12809
|
+
return make_link(result.fullName, "module");
|
|
12810
|
+
case "TypedefDeclaration":
|
|
12811
|
+
return make_link(result.fullName, "named_type");
|
|
12812
|
+
case "VariableDeclarator":
|
|
12813
|
+
return make_link(result.fullName, "var");
|
|
12814
|
+
}
|
|
12815
|
+
return null;
|
|
12816
|
+
}
|
|
12817
|
+
function lookupByFullName(state, fullName) {
|
|
12818
|
+
return fullName.split(".").reduce((results, part) => {
|
|
12819
|
+
return results
|
|
12820
|
+
.flatMap((result) => isStateNode(result)
|
|
12821
|
+
? result.decls?.[part] || result.type_decls?.[part]
|
|
12822
|
+
: null)
|
|
12823
|
+
.filter((sn) => !!sn);
|
|
12824
|
+
}, [state.stack[0].sn]);
|
|
12825
|
+
}
|
|
9762
12826
|
function handleException(state, node, exception) {
|
|
9763
12827
|
try {
|
|
9764
12828
|
const fullName = state.stack
|
|
9765
|
-
.map((e) => e.name)
|
|
12829
|
+
.map((e) => e.sn.name)
|
|
9766
12830
|
.concat("name" in node && typeof node.name === "string" ? node.name : undefined)
|
|
9767
12831
|
.filter((e) => e != null)
|
|
9768
12832
|
.join(".");
|
|
@@ -9787,7 +12851,7 @@ function handleException(state, node, exception) {
|
|
|
9787
12851
|
function findUsing(state, stack, using) {
|
|
9788
12852
|
if (using.module)
|
|
9789
12853
|
return using.module;
|
|
9790
|
-
let module = stack[0];
|
|
12854
|
+
let module = stack[0].sn;
|
|
9791
12855
|
const find = (node) => {
|
|
9792
12856
|
let name;
|
|
9793
12857
|
if (node.type === "Identifier") {
|
|
@@ -9835,7 +12899,7 @@ function findUsingForNode(state, stack, i, node) {
|
|
|
9835
12899
|
imports = [];
|
|
9836
12900
|
imports.push({
|
|
9837
12901
|
name: `${module.fullName}.${node.name}`,
|
|
9838
|
-
decls: { parent: si, results: module.type_decls[node.name] },
|
|
12902
|
+
decls: { parent: si.sn, results: module.type_decls[node.name] },
|
|
9839
12903
|
});
|
|
9840
12904
|
}
|
|
9841
12905
|
}
|
|
@@ -9857,7 +12921,7 @@ function getApiFunctionInfo(state, func) {
|
|
|
9857
12921
|
};
|
|
9858
12922
|
}
|
|
9859
12923
|
if (func.name === "initialize") {
|
|
9860
|
-
const top = func.stack[func.stack.length - 1];
|
|
12924
|
+
const top = func.stack[func.stack.length - 1].sn;
|
|
9861
12925
|
if (top.type === "ClassDeclaration") {
|
|
9862
12926
|
top.hasInvoke = true;
|
|
9863
12927
|
}
|
|
@@ -9870,7 +12934,7 @@ function markInvokeClassMethod(state, func) {
|
|
|
9870
12934
|
func.info = state.invokeInfo;
|
|
9871
12935
|
}
|
|
9872
12936
|
function isLocal(v) {
|
|
9873
|
-
return v.stack[v.stack.length - 1]?.type === "BlockStatement";
|
|
12937
|
+
return v.stack[v.stack.length - 1]?.sn.type === "BlockStatement";
|
|
9874
12938
|
}
|
|
9875
12939
|
function diagnostic(state, node, message, type = "INFO", extra) {
|
|
9876
12940
|
if (!state.diagnostics)
|