@markw65/monkeyc-optimizer 1.1.6 → 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 +18 -1
- package/build/api.cjs +3302 -242
- package/build/optimizer.cjs +62 -61
- 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 &&
|
|
@@ -1851,9 +1851,7 @@ function findCalleesByNode(state, callee) {
|
|
|
1851
1851
|
/* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
|
|
1852
1852
|
/* harmony import */ var _ast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6652);
|
|
1853
1853
|
/* harmony import */ var _function_info__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(819);
|
|
1854
|
-
/* harmony import */ var
|
|
1855
|
-
/* harmony import */ var _variable_renamer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4405);
|
|
1856
|
-
|
|
1854
|
+
/* harmony import */ var _variable_renamer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4405);
|
|
1857
1855
|
|
|
1858
1856
|
|
|
1859
1857
|
|
|
@@ -1936,9 +1934,9 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
1936
1934
|
// if decl is a local, it also can't be changed
|
|
1937
1935
|
// by a call to another function.
|
|
1938
1936
|
for (let i = 0;; i++) {
|
|
1939
|
-
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
1937
|
+
if (!state.stack[i] || decl.stack[i]?.sn !== state.stack[i].sn)
|
|
1940
1938
|
return false;
|
|
1941
|
-
if (state.stack[i].type === "FunctionDeclaration")
|
|
1939
|
+
if (state.stack[i].sn.type === "FunctionDeclaration")
|
|
1942
1940
|
return true;
|
|
1943
1941
|
}
|
|
1944
1942
|
}
|
|
@@ -2075,7 +2073,7 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
2075
2073
|
}
|
|
2076
2074
|
return null;
|
|
2077
2075
|
};
|
|
2078
|
-
state.stack = func.stack;
|
|
2076
|
+
state.stack = func.stack.concat({ sn: func });
|
|
2079
2077
|
state.traverse(func.node.body);
|
|
2080
2078
|
}
|
|
2081
2079
|
finally {
|
|
@@ -2186,9 +2184,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
2186
2184
|
// lookup determines static-ness of the lookup context based on seeing
|
|
2187
2185
|
// a static FunctionDeclaration, but the FunctionDeclaration's stack
|
|
2188
2186
|
// doesn't include the FunctionDeclaration itself.
|
|
2189
|
-
const
|
|
2190
|
-
? func.stack.concat(func)
|
|
2191
|
-
: func.stack;
|
|
2187
|
+
const lookupStack = func.stack.concat({ sn: func });
|
|
2192
2188
|
try {
|
|
2193
2189
|
state.pre = (node) => {
|
|
2194
2190
|
if (failed)
|
|
@@ -2244,7 +2240,7 @@ function processInlineBody(state, func, call, root, params) {
|
|
|
2244
2240
|
}
|
|
2245
2241
|
return null;
|
|
2246
2242
|
}
|
|
2247
|
-
const replacement = fixNodeScope(state, node,
|
|
2243
|
+
const replacement = fixNodeScope(state, node, lookupStack);
|
|
2248
2244
|
if (!replacement) {
|
|
2249
2245
|
failed = true;
|
|
2250
2246
|
inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
|
|
@@ -2578,10 +2574,11 @@ function inlineFunction(state, func, call, context) {
|
|
|
2578
2574
|
if (!typecheckFalse) {
|
|
2579
2575
|
return ret;
|
|
2580
2576
|
}
|
|
2581
|
-
const
|
|
2582
|
-
if (!
|
|
2577
|
+
const callerElem = state.stack.find((elem) => elem.sn.type === "FunctionDeclaration");
|
|
2578
|
+
if (!callerElem) {
|
|
2583
2579
|
return ret;
|
|
2584
2580
|
}
|
|
2581
|
+
const callerSn = callerElem.sn;
|
|
2585
2582
|
const caller = callerSn.node;
|
|
2586
2583
|
if (!caller.attrs) {
|
|
2587
2584
|
caller.attrs = withLoc({
|
|
@@ -2703,85 +2700,2095 @@ function fixNodeScope(state, lookupNode, nodeStack) {
|
|
|
2703
2700
|
|
|
2704
2701
|
/***/ }),
|
|
2705
2702
|
|
|
2706
|
-
/***/
|
|
2703
|
+
/***/ 5530:
|
|
2707
2704
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
2708
2705
|
|
|
2709
2706
|
"use strict";
|
|
2710
2707
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
2711
|
-
/* harmony export */ "
|
|
2708
|
+
/* harmony export */ "BJ": () => (/* binding */ analyze)
|
|
2712
2709
|
/* harmony export */ });
|
|
2713
|
-
/*
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
*/
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
"Toybox.Communications.NETWORK_RESPONSE_OUT_OF_MEMORY",
|
|
2736
|
-
"Toybox.Communications.BLE_REQUEST_CANCELLED",
|
|
2737
|
-
"Toybox.Communications.INVALID_HTTP_METHOD_IN_REQUEST",
|
|
2738
|
-
"Toybox.Communications.BLE_NO_DATA",
|
|
2739
|
-
"Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_REQUEST",
|
|
2740
|
-
"Toybox.Communications.BLE_ERROR",
|
|
2741
|
-
"Toybox.Communications.NETWORK_RESPONSE_TOO_LARGE",
|
|
2742
|
-
"Toybox.Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE",
|
|
2743
|
-
"Toybox.Communications.BLE_REQUEST_TOO_LARGE",
|
|
2744
|
-
"Toybox.Communications.UNABLE_TO_PROCESS_MEDIA",
|
|
2745
|
-
"Toybox.Communications.REQUEST_CONNECTION_DROPPED",
|
|
2746
|
-
"Toybox.Communications.BLE_UNKNOWN_SEND_ERROR",
|
|
2747
|
-
"Toybox.Communications.BLE_QUEUE_FULL",
|
|
2748
|
-
"Toybox.Communications.STORAGE_FULL",
|
|
2749
|
-
"Toybox.Communications.BLE_SERVER_TIMEOUT",
|
|
2750
|
-
"Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_NETWORK_RESPONSE",
|
|
2751
|
-
"Toybox.Communications.SECURE_CONNECTION_REQUIRED",
|
|
2752
|
-
"Toybox.Communications.NETWORK_REQUEST_TIMED_OUT",
|
|
2753
|
-
"Toybox.Communications.BLE_HOST_TIMEOUT",
|
|
2754
|
-
"Toybox.Communications.UNABLE_TO_PROCESS_HLS",
|
|
2755
|
-
"Toybox.Graphics.COLOR_TRANSPARENT",
|
|
2756
|
-
"Toybox.AntPlus.INVALID_SPEED",
|
|
2757
|
-
"Toybox.AntPlus.INVALID_CADENCE",
|
|
2758
|
-
"Toybox.WatchUi.LAYOUT_VALIGN_START",
|
|
2759
|
-
"Toybox.WatchUi.LAYOUT_VALIGN_TOP",
|
|
2760
|
-
"Toybox.WatchUi.LAYOUT_VALIGN_BOTTOM",
|
|
2761
|
-
"Toybox.WatchUi.LAYOUT_VALIGN_CENTER",
|
|
2762
|
-
"Toybox.WatchUi.LAYOUT_HALIGN_RIGHT",
|
|
2763
|
-
"Toybox.WatchUi.LAYOUT_HALIGN_CENTER",
|
|
2764
|
-
"Toybox.WatchUi.LAYOUT_HALIGN_START",
|
|
2765
|
-
"Toybox.WatchUi.LAYOUT_HALIGN_LEFT",
|
|
2766
|
-
];
|
|
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);
|
|
2767
2732
|
|
|
2768
2733
|
|
|
2769
|
-
/***/ }),
|
|
2770
2734
|
|
|
2771
|
-
/***/ 9697:
|
|
2772
|
-
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
2773
2735
|
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
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
|
+
|
|
3817
|
+
/***/ }),
|
|
3818
|
+
|
|
3819
|
+
/***/ 4499:
|
|
3820
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3821
|
+
|
|
3822
|
+
"use strict";
|
|
3823
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
3824
|
+
/* harmony export */ "k": () => (/* binding */ negativeFixups)
|
|
3825
|
+
/* harmony export */ });
|
|
3826
|
+
/*
|
|
3827
|
+
* This is strange. It pretty much has to be a bug in the sdk,
|
|
3828
|
+
* but its the same in every sdk.
|
|
3829
|
+
*
|
|
3830
|
+
* ${sdk}/bin/api.mir describes the Toybox api down to every module,
|
|
3831
|
+
* class, function, enum and constant, together with the enum
|
|
3832
|
+
* and constant values.
|
|
3833
|
+
*
|
|
3834
|
+
* The problem is that every enum or constant with a negative
|
|
3835
|
+
* value, appears with the corresponding positive value in api.mir.
|
|
3836
|
+
* So eg Graphics.COLOR_TRANSPARENT should be -1, but instead, it has
|
|
3837
|
+
* the value 1.
|
|
3838
|
+
*
|
|
3839
|
+
* This is a (currently somewhat ad-hoc) list of the negative constants
|
|
3840
|
+
* so we can fix them after reading api.mir.
|
|
3841
|
+
*/
|
|
3842
|
+
const negativeFixups = [
|
|
3843
|
+
"Toybox.Communications.BLE_CONNECTION_UNAVAILABLE",
|
|
3844
|
+
"Toybox.Communications.INVALID_HTTP_BODY_IN_REQUEST",
|
|
3845
|
+
"Toybox.Communications.REQUEST_CANCELLED",
|
|
3846
|
+
"Toybox.Communications.UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE",
|
|
3847
|
+
"Toybox.Communications.UNABLE_TO_PROCESS_IMAGE",
|
|
3848
|
+
"Toybox.Communications.NETWORK_RESPONSE_OUT_OF_MEMORY",
|
|
3849
|
+
"Toybox.Communications.BLE_REQUEST_CANCELLED",
|
|
3850
|
+
"Toybox.Communications.INVALID_HTTP_METHOD_IN_REQUEST",
|
|
3851
|
+
"Toybox.Communications.BLE_NO_DATA",
|
|
3852
|
+
"Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_REQUEST",
|
|
3853
|
+
"Toybox.Communications.BLE_ERROR",
|
|
3854
|
+
"Toybox.Communications.NETWORK_RESPONSE_TOO_LARGE",
|
|
3855
|
+
"Toybox.Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE",
|
|
3856
|
+
"Toybox.Communications.BLE_REQUEST_TOO_LARGE",
|
|
3857
|
+
"Toybox.Communications.UNABLE_TO_PROCESS_MEDIA",
|
|
3858
|
+
"Toybox.Communications.REQUEST_CONNECTION_DROPPED",
|
|
3859
|
+
"Toybox.Communications.BLE_UNKNOWN_SEND_ERROR",
|
|
3860
|
+
"Toybox.Communications.BLE_QUEUE_FULL",
|
|
3861
|
+
"Toybox.Communications.STORAGE_FULL",
|
|
3862
|
+
"Toybox.Communications.BLE_SERVER_TIMEOUT",
|
|
3863
|
+
"Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_NETWORK_RESPONSE",
|
|
3864
|
+
"Toybox.Communications.SECURE_CONNECTION_REQUIRED",
|
|
3865
|
+
"Toybox.Communications.NETWORK_REQUEST_TIMED_OUT",
|
|
3866
|
+
"Toybox.Communications.BLE_HOST_TIMEOUT",
|
|
3867
|
+
"Toybox.Communications.UNABLE_TO_PROCESS_HLS",
|
|
3868
|
+
"Toybox.Graphics.COLOR_TRANSPARENT",
|
|
3869
|
+
"Toybox.AntPlus.INVALID_SPEED",
|
|
3870
|
+
"Toybox.AntPlus.INVALID_CADENCE",
|
|
3871
|
+
"Toybox.WatchUi.LAYOUT_VALIGN_START",
|
|
3872
|
+
"Toybox.WatchUi.LAYOUT_VALIGN_TOP",
|
|
3873
|
+
"Toybox.WatchUi.LAYOUT_VALIGN_BOTTOM",
|
|
3874
|
+
"Toybox.WatchUi.LAYOUT_VALIGN_CENTER",
|
|
3875
|
+
"Toybox.WatchUi.LAYOUT_HALIGN_RIGHT",
|
|
3876
|
+
"Toybox.WatchUi.LAYOUT_HALIGN_CENTER",
|
|
3877
|
+
"Toybox.WatchUi.LAYOUT_HALIGN_START",
|
|
3878
|
+
"Toybox.WatchUi.LAYOUT_HALIGN_LEFT",
|
|
3879
|
+
];
|
|
3880
|
+
|
|
3881
|
+
|
|
3882
|
+
/***/ }),
|
|
3883
|
+
|
|
3884
|
+
/***/ 9697:
|
|
3885
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3886
|
+
|
|
3887
|
+
"use strict";
|
|
3888
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
3889
|
+
/* harmony export */ "m": () => (/* binding */ StateNodeAttributes)
|
|
3890
|
+
/* harmony export */ });
|
|
3891
|
+
var StateNodeAttributes;
|
|
3892
|
+
(function (StateNodeAttributes) {
|
|
3893
|
+
StateNodeAttributes[StateNodeAttributes["PUBLIC"] = 1] = "PUBLIC";
|
|
3894
|
+
StateNodeAttributes[StateNodeAttributes["PROTECTED"] = 2] = "PROTECTED";
|
|
3895
|
+
StateNodeAttributes[StateNodeAttributes["PRIVATE"] = 4] = "PRIVATE";
|
|
3896
|
+
StateNodeAttributes[StateNodeAttributes["STATIC"] = 8] = "STATIC";
|
|
3897
|
+
})(StateNodeAttributes || (StateNodeAttributes = {}));
|
|
3898
|
+
|
|
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
|
+
}
|
|
2785
4792
|
|
|
2786
4793
|
|
|
2787
4794
|
/***/ }),
|
|
@@ -3093,7 +5100,7 @@ function filterDecls(decls, possible, name) {
|
|
|
3093
5100
|
const stack = d.stack;
|
|
3094
5101
|
possible.forEach((poss) => {
|
|
3095
5102
|
for (let i = stack.length; i--;) {
|
|
3096
|
-
const sn = stack[i];
|
|
5103
|
+
const sn = stack[i].sn;
|
|
3097
5104
|
if (sn.decls === poss.decls) {
|
|
3098
5105
|
if (!cur[0]) {
|
|
3099
5106
|
cur = [new Set(), new Set()];
|
|
@@ -3314,9 +5321,21 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
3314
5321
|
if (Array.isArray(decl) ||
|
|
3315
5322
|
(decl.type !== "MemberDecl" && decl.type !== "Unknown")) {
|
|
3316
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
|
+
*/
|
|
3317
5328
|
const v = blockState.get(decl);
|
|
3318
5329
|
if (v?.equivSet) {
|
|
3319
|
-
|
|
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);
|
|
3320
5339
|
return;
|
|
3321
5340
|
}
|
|
3322
5341
|
}
|
|
@@ -3422,7 +5441,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
|
|
|
3422
5441
|
return tmpState;
|
|
3423
5442
|
}
|
|
3424
5443
|
if (isExact(right) &&
|
|
3425
|
-
right.type &
|
|
5444
|
+
right.type & SingletonTypeTagsConst &&
|
|
3426
5445
|
left.type & right.type) {
|
|
3427
5446
|
// left is not equal to right, and right is one of the
|
|
3428
5447
|
// singleton types; so we can remove that type from left.
|
|
@@ -4340,7 +6359,7 @@ function common_types(left, right, allowed) {
|
|
|
4340
6359
|
}
|
|
4341
6360
|
if (lt & 8 /* TypeTag.Number */) {
|
|
4342
6361
|
result |=
|
|
4343
|
-
rt & 6 /* TypeTag.Boolean */ ? 6 /* TypeTag.Boolean */ : rt & ~
|
|
6362
|
+
rt & 6 /* TypeTag.Boolean */ ? 6 /* TypeTag.Boolean */ : rt & ~SingletonTypeTagsConst;
|
|
4344
6363
|
}
|
|
4345
6364
|
if (lt & 16 /* TypeTag.Long */) {
|
|
4346
6365
|
if (rt & 6 /* TypeTag.Boolean */) {
|
|
@@ -4750,7 +6769,7 @@ function calleeObjectType(istate, callee) {
|
|
|
4750
6769
|
}
|
|
4751
6770
|
if (callee.type === "Identifier" && istate.func) {
|
|
4752
6771
|
const func = istate.func;
|
|
4753
|
-
const [self] = func.stack.slice(-1);
|
|
6772
|
+
const [{ sn: self }] = func.stack.slice(-1);
|
|
4754
6773
|
return typeFromTypeStateNode(istate.state, self, (func.attributes & StateNodeAttributes.STATIC) !== 0 ||
|
|
4755
6774
|
self.type !== "ClassDeclaration");
|
|
4756
6775
|
}
|
|
@@ -4882,14 +6901,14 @@ function checkCallArgs(istate, node, callees, args) {
|
|
|
4882
6901
|
return resultType;
|
|
4883
6902
|
}
|
|
4884
6903
|
function isOverride(cur, funcs) {
|
|
4885
|
-
const cls = cur.stack?.[cur.stack.length - 1];
|
|
6904
|
+
const cls = cur.stack?.[cur.stack.length - 1]?.sn;
|
|
4886
6905
|
if (cls?.type === "ClassDeclaration" && cls.superClasses) {
|
|
4887
6906
|
const supers = getSuperClasses(cls);
|
|
4888
6907
|
if (supers &&
|
|
4889
6908
|
some(funcs, (func) => {
|
|
4890
6909
|
if (func === cur)
|
|
4891
6910
|
return false;
|
|
4892
|
-
const fcls = func.stack?.[func.stack.length - 1];
|
|
6911
|
+
const fcls = func.stack?.[func.stack.length - 1].sn;
|
|
4893
6912
|
return fcls ? supers.has(fcls) : false;
|
|
4894
6913
|
})) {
|
|
4895
6914
|
return true;
|
|
@@ -5663,7 +7682,7 @@ function evaluateNode(istate, node) {
|
|
|
5663
7682
|
case "ThisExpression": {
|
|
5664
7683
|
const self = (() => {
|
|
5665
7684
|
for (let i = state.stack.length; i--;) {
|
|
5666
|
-
const si = state.stack[i];
|
|
7685
|
+
const si = state.stack[i].sn;
|
|
5667
7686
|
if (si.type === "ClassDeclaration") {
|
|
5668
7687
|
const klass = { type: 16384 /* TypeTag.Class */, value: si };
|
|
5669
7688
|
if ((istate.func?.attributes || 0) & StateNodeAttributes.STATIC) {
|
|
@@ -5873,7 +7892,7 @@ function evaluateNode(istate, node) {
|
|
|
5873
7892
|
if (node.init) {
|
|
5874
7893
|
const init = popIstate(istate, node.init);
|
|
5875
7894
|
if (node.id.type === "BinaryExpression" && istate.typeChecker) {
|
|
5876
|
-
const top = istate.state.
|
|
7895
|
+
const top = istate.state.top().sn;
|
|
5877
7896
|
if (top.type !== "BlockStatement") {
|
|
5878
7897
|
const declType = typeFromTypespec(istate.state, node.id.right);
|
|
5879
7898
|
if (!istate.typeChecker(init.value, declType)) {
|
|
@@ -6462,58 +8481,692 @@ function restrictExactTypesByEquality(a, b) {
|
|
|
6462
8481
|
default:
|
|
6463
8482
|
unhandledType(a);
|
|
6464
8483
|
}
|
|
6465
|
-
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;
|
|
6466
8984
|
}
|
|
6467
|
-
function
|
|
6468
|
-
let
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
// specialization, the result is just b.
|
|
6473
|
-
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;
|
|
6474
8990
|
}
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
if (brt.type !== 0 /* TypeTag.Never */) {
|
|
6484
|
-
if (!br) {
|
|
6485
|
-
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;
|
|
6486
8999
|
}
|
|
6487
|
-
|
|
6488
|
-
|
|
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);
|
|
6489
9029
|
}
|
|
6490
|
-
}
|
|
6491
|
-
bits = next;
|
|
6492
|
-
} while (bits);
|
|
6493
|
-
if (!br) {
|
|
6494
|
-
return { type: 0 /* TypeTag.Never */ };
|
|
6495
9030
|
}
|
|
6496
|
-
return
|
|
9031
|
+
return tryIdentity(istate, node, left, right);
|
|
6497
9032
|
}
|
|
6498
9033
|
/*
|
|
6499
|
-
*
|
|
6500
|
-
*
|
|
9034
|
+
* Try to reorder (a op K1) op K2 => a op (K1 op K2),
|
|
9035
|
+
* and fold K1 op K2.
|
|
6501
9036
|
*
|
|
6502
|
-
*
|
|
6503
|
-
*
|
|
6504
|
-
*
|
|
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.
|
|
6505
9040
|
*
|
|
6506
|
-
*
|
|
6507
|
-
*
|
|
6508
|
-
*
|
|
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.
|
|
6509
9053
|
*/
|
|
6510
|
-
function
|
|
6511
|
-
if (
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
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;
|
|
6515
9060
|
}
|
|
6516
|
-
|
|
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;
|
|
6517
9170
|
}
|
|
6518
9171
|
|
|
6519
9172
|
|
|
@@ -6759,7 +9412,7 @@ function printBlockTrailer(block) {
|
|
|
6759
9412
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
6760
9413
|
/* harmony export */ "iX": () => (/* binding */ getStateNodeDeclsFromType)
|
|
6761
9414
|
/* harmony export */ });
|
|
6762
|
-
/* 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 */
|
|
6763
9416
|
/* harmony import */ var _markw65_prettier_plugin_monkeyc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9642);
|
|
6764
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__);
|
|
6765
9418
|
/* harmony import */ var _data_flow__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8180);
|
|
@@ -6834,7 +9487,7 @@ function typeTagName(tag) {
|
|
|
6834
9487
|
}
|
|
6835
9488
|
}
|
|
6836
9489
|
const LastTypeTag = 262144 /* TypeTag.Typedef */;
|
|
6837
|
-
const
|
|
9490
|
+
const SingletonTypeTagsConst = 1 /* TypeTag.Null */ | 2 /* TypeTag.False */ | 4 /* TypeTag.True */;
|
|
6838
9491
|
const UnionDataTypeTagsConst = 512 /* TypeTag.Array */ |
|
|
6839
9492
|
1024 /* TypeTag.Dictionary */ |
|
|
6840
9493
|
2048 /* TypeTag.Method */ |
|
|
@@ -6857,7 +9510,7 @@ const ObjectLikeTagsConst = 6 /* TypeTag.Boolean */ |
|
|
|
6857
9510
|
1024 /* TypeTag.Dictionary */ |
|
|
6858
9511
|
2048 /* TypeTag.Method */ |
|
|
6859
9512
|
65536 /* TypeTag.Enum */;
|
|
6860
|
-
const EnumTagsConst =
|
|
9513
|
+
const EnumTagsConst = SingletonTypeTagsConst | (ValueTypeTagsConst & ~131072 /* TypeTag.Symbol */);
|
|
6861
9514
|
function isExact(v) {
|
|
6862
9515
|
// check that there is exactly one bit set
|
|
6863
9516
|
return v.type !== 0 && !(v.type & (v.type - 1));
|
|
@@ -6866,27 +9519,18 @@ function isUnion(v) {
|
|
|
6866
9519
|
return v.type !== 0 && !isExact(v);
|
|
6867
9520
|
}
|
|
6868
9521
|
function isSingleton(v) {
|
|
6869
|
-
return isExact(v) && (v.type &
|
|
9522
|
+
return isExact(v) && (v.type & SingletonTypeTagsConst) !== 0;
|
|
6870
9523
|
}
|
|
6871
9524
|
function hasValue(v) {
|
|
6872
9525
|
return (isExact(v) &&
|
|
6873
|
-
((v.type &
|
|
9526
|
+
((v.type & SingletonTypeTagsConst) !== 0 || v.value !== undefined));
|
|
6874
9527
|
}
|
|
6875
9528
|
function hasNoData(v, t) {
|
|
6876
9529
|
if (v.value == null)
|
|
6877
9530
|
return true;
|
|
6878
9531
|
return ((hasUnionData(v.type)
|
|
6879
9532
|
? v.value.mask & t
|
|
6880
|
-
: v.type & t & ~
|
|
6881
|
-
}
|
|
6882
|
-
function lookupByFullName(state, fullName) {
|
|
6883
|
-
return fullName.split(".").reduce((results, part) => {
|
|
6884
|
-
return results
|
|
6885
|
-
.flatMap((result) => (0,_api__WEBPACK_IMPORTED_MODULE_2__.isStateNode)(result)
|
|
6886
|
-
? result.decls?.[part] || result.type_decls?.[part]
|
|
6887
|
-
: null)
|
|
6888
|
-
.filter((sn) => !!sn);
|
|
6889
|
-
}, [state.stack[0]]);
|
|
9533
|
+
: v.type & t & ~SingletonTypeTagsConst) === 0);
|
|
6890
9534
|
}
|
|
6891
9535
|
function cloneType(t) {
|
|
6892
9536
|
return { ...t };
|
|
@@ -7455,7 +10099,7 @@ function getObjectValue(t) {
|
|
|
7455
10099
|
}
|
|
7456
10100
|
function forEachUnionComponent(v, bits, fn) {
|
|
7457
10101
|
// never iterate the singleton bits, because they don't have data
|
|
7458
|
-
bits &= ~
|
|
10102
|
+
bits &= ~SingletonTypeTagsConst;
|
|
7459
10103
|
if (!bits)
|
|
7460
10104
|
return;
|
|
7461
10105
|
if ((v.type | bits) & UnionDataTypeTagsConst) {
|
|
@@ -7485,7 +10129,7 @@ function forEachUnionComponent(v, bits, fn) {
|
|
|
7485
10129
|
function getUnionComponent(v, tag) {
|
|
7486
10130
|
if (v.value == null)
|
|
7487
10131
|
return null;
|
|
7488
|
-
let bits = v.type & ~
|
|
10132
|
+
let bits = v.type & ~SingletonTypeTagsConst;
|
|
7489
10133
|
if (!bits)
|
|
7490
10134
|
return null;
|
|
7491
10135
|
if (bits & (bits - 1)) {
|
|
@@ -7559,7 +10203,7 @@ function getStateNodeDeclsFromType(state, object) {
|
|
|
7559
10203
|
next &= ~6 /* TypeTag.Boolean */;
|
|
7560
10204
|
}
|
|
7561
10205
|
const name = `Toybox.Lang.${typeTagName(bit)}`;
|
|
7562
|
-
const sns = lookupByFullName(state, name);
|
|
10206
|
+
const sns = (0,_api__WEBPACK_IMPORTED_MODULE_2__.lookupByFullName)(state, name);
|
|
7563
10207
|
sns.forEach((sn) => (0,_api__WEBPACK_IMPORTED_MODULE_2__.isStateNode)(sn) && decls.push(sn));
|
|
7564
10208
|
bits = next;
|
|
7565
10209
|
} while (bits);
|
|
@@ -7600,7 +10244,7 @@ function unionInto(to, from) {
|
|
|
7600
10244
|
return true;
|
|
7601
10245
|
}
|
|
7602
10246
|
const newTags = to.type | from.type;
|
|
7603
|
-
if (!(from.type & ~
|
|
10247
|
+
if (!(from.type & ~SingletonTypeTagsConst)) {
|
|
7604
10248
|
// - Adding singletons never affects the data.
|
|
7605
10249
|
if (newTags === to.type)
|
|
7606
10250
|
return false;
|
|
@@ -7862,7 +10506,7 @@ function clearValuesUnder(v, tag, clearTag = false) {
|
|
|
7862
10506
|
const newTag = clearTag ? v.type & ~tag : v.type | tag;
|
|
7863
10507
|
// If the incoming type consists of singletons,
|
|
7864
10508
|
// we can always merge it without affecting our data.
|
|
7865
|
-
tag &= ~
|
|
10509
|
+
tag &= ~SingletonTypeTagsConst;
|
|
7866
10510
|
if (!tag || v.value == null) {
|
|
7867
10511
|
v.type = newTag;
|
|
7868
10512
|
return;
|
|
@@ -7983,6 +10627,193 @@ function widenType(t) {
|
|
|
7983
10627
|
}
|
|
7984
10628
|
|
|
7985
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
|
+
|
|
7986
10817
|
/***/ }),
|
|
7987
10818
|
|
|
7988
10819
|
/***/ 4405:
|
|
@@ -8032,7 +10863,7 @@ function renameVariable(state, locals, declName) {
|
|
|
8032
10863
|
let ok = false;
|
|
8033
10864
|
let i;
|
|
8034
10865
|
for (i = state.stack.length; i--;) {
|
|
8035
|
-
const elm = state.stack[i];
|
|
10866
|
+
const elm = state.stack[i].sn;
|
|
8036
10867
|
if (ok) {
|
|
8037
10868
|
if (hasProperty(elm.decls, name)) {
|
|
8038
10869
|
break;
|
|
@@ -8167,10 +10998,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
8167
10998
|
__webpack_require__.d(__webpack_exports__, {
|
|
8168
10999
|
"checkCompilerVersion": () => (/* binding */ checkCompilerVersion),
|
|
8169
11000
|
"collectNamespaces": () => (/* binding */ collectNamespaces),
|
|
11001
|
+
"createDocumentationMap": () => (/* binding */ createDocumentationMap),
|
|
8170
11002
|
"diagnostic": () => (/* binding */ diagnostic),
|
|
8171
11003
|
"diagnosticHelper": () => (/* binding */ diagnosticHelper),
|
|
11004
|
+
"findNamesInScope": () => (/* binding */ findNamesInScope),
|
|
8172
11005
|
"findUsingForNode": () => (/* binding */ findUsingForNode),
|
|
8173
11006
|
"formatAst": () => (/* binding */ formatAst),
|
|
11007
|
+
"formatAstLongLines": () => (/* binding */ formatAstLongLines),
|
|
8174
11008
|
"getApiFunctionInfo": () => (/* binding */ getApiFunctionInfo),
|
|
8175
11009
|
"getApiMapping": () => (/* binding */ getApiMapping),
|
|
8176
11010
|
"getSuperClasses": () => (/* binding */ getSuperClasses),
|
|
@@ -8178,7 +11012,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
8178
11012
|
"isLocal": () => (/* binding */ isLocal),
|
|
8179
11013
|
"isLookupCandidate": () => (/* binding */ isLookupCandidate),
|
|
8180
11014
|
"isStateNode": () => (/* binding */ isStateNode),
|
|
11015
|
+
"lookupByFullName": () => (/* binding */ lookupByFullName),
|
|
8181
11016
|
"lookupNext": () => (/* binding */ lookupNext),
|
|
11017
|
+
"lookupResultContains": () => (/* binding */ lookupResultContains),
|
|
11018
|
+
"lookupWithType": () => (/* binding */ lookupWithType),
|
|
11019
|
+
"makeToyboxLink": () => (/* binding */ makeToyboxLink),
|
|
11020
|
+
"mapVarDeclsByType": () => (/* binding */ mapVarDeclsByType),
|
|
8182
11021
|
"markInvokeClassMethod": () => (/* binding */ markInvokeClassMethod),
|
|
8183
11022
|
"parseSdkVersion": () => (/* binding */ parseSdkVersion),
|
|
8184
11023
|
"sameLookupResult": () => (/* binding */ sameLookupResult),
|
|
@@ -8198,6 +11037,8 @@ var promises_ = __webpack_require__(560);
|
|
|
8198
11037
|
const external_prettier_namespaceObject = require("prettier");
|
|
8199
11038
|
// EXTERNAL MODULE: ./src/ast.ts
|
|
8200
11039
|
var src_ast = __webpack_require__(6652);
|
|
11040
|
+
// EXTERNAL MODULE: ./src/mc-rewrite.ts
|
|
11041
|
+
var mc_rewrite = __webpack_require__(5530);
|
|
8201
11042
|
// EXTERNAL MODULE: ./src/negative-fixups.ts
|
|
8202
11043
|
var negative_fixups = __webpack_require__(4499);
|
|
8203
11044
|
// EXTERNAL MODULE: ./src/optimizer-types.ts
|
|
@@ -8721,13 +11562,14 @@ function add_one_resource(state, doc, module, e) {
|
|
|
8721
11562
|
}
|
|
8722
11563
|
}
|
|
8723
11564
|
|
|
8724
|
-
// EXTERNAL MODULE: external "./util.cjs"
|
|
8725
|
-
var external_util_cjs_ = __webpack_require__(6906);
|
|
8726
11565
|
// EXTERNAL MODULE: ./src/type-flow.ts
|
|
8727
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);
|
|
8728
11571
|
;// CONCATENATED MODULE: ./src/visitor.ts
|
|
8729
11572
|
|
|
8730
|
-
|
|
8731
11573
|
function visitorNode(node) {
|
|
8732
11574
|
if (node.type === "Identifier") {
|
|
8733
11575
|
return node;
|
|
@@ -8746,28 +11588,11 @@ function visitorNode(node) {
|
|
|
8746
11588
|
}
|
|
8747
11589
|
return node;
|
|
8748
11590
|
}
|
|
8749
|
-
function visitReferences(state, ast, name, defn, callback, includeDefs = false, filter = null, typeMap = null) {
|
|
8750
|
-
const lookup = (node, nonLocal = false) =>
|
|
8751
|
-
const results = nonLocal ? state.lookupNonlocal(node) : state.lookup(node);
|
|
8752
|
-
if (results[1] || !typeMap)
|
|
8753
|
-
return results;
|
|
8754
|
-
if (node.type === "MemberExpression" && !node.computed) {
|
|
8755
|
-
const objectType = typeMap.get(node.object);
|
|
8756
|
-
if (!objectType)
|
|
8757
|
-
return results;
|
|
8758
|
-
const [, decls] = (0,type_flow/* findObjectDeclsByProperty */.nK)(state, objectType, node);
|
|
8759
|
-
if (decls) {
|
|
8760
|
-
const next = (0,external_api_cjs_.lookupNext)(state, [{ parent: null, results: decls }], "decls", node.property);
|
|
8761
|
-
if (next) {
|
|
8762
|
-
return [node.property.name, next];
|
|
8763
|
-
}
|
|
8764
|
-
}
|
|
8765
|
-
}
|
|
8766
|
-
return results;
|
|
8767
|
-
};
|
|
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);
|
|
8768
11593
|
const checkResults = ([name, results], node) => {
|
|
8769
11594
|
if (name && results) {
|
|
8770
|
-
if (!defn || (0,external_api_cjs_.
|
|
11595
|
+
if (!defn || (0,external_api_cjs_.lookupResultContains)(results, defn)) {
|
|
8771
11596
|
if (callback(node, results, false) === false) {
|
|
8772
11597
|
return [];
|
|
8773
11598
|
}
|
|
@@ -8780,6 +11605,35 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8780
11605
|
}
|
|
8781
11606
|
return null;
|
|
8782
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
|
+
};
|
|
8783
11637
|
state.pre = (node) => {
|
|
8784
11638
|
if (filter && !filter(node))
|
|
8785
11639
|
return [];
|
|
@@ -8816,7 +11670,8 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8816
11670
|
// it will be ignored, and the lookup will start as if the
|
|
8817
11671
|
// call had been written self.foo() rather than foo().
|
|
8818
11672
|
if (node.callee.type === "Identifier") {
|
|
8819
|
-
if (!name || node.callee.name === name)
|
|
11673
|
+
if ((!name || node.callee.name === name) &&
|
|
11674
|
+
(!filter || filter(node.callee))) {
|
|
8820
11675
|
/* ignore return value */
|
|
8821
11676
|
checkResults(lookup(node.callee, true), node.callee);
|
|
8822
11677
|
}
|
|
@@ -8852,29 +11707,47 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8852
11707
|
return ["returnType"];
|
|
8853
11708
|
}
|
|
8854
11709
|
case "ModuleDeclaration":
|
|
8855
|
-
if (includeDefs)
|
|
8856
|
-
|
|
11710
|
+
if (includeDefs) {
|
|
11711
|
+
visitDef(node.id);
|
|
11712
|
+
}
|
|
8857
11713
|
return ["body"];
|
|
8858
11714
|
case "ClassDeclaration":
|
|
8859
|
-
if (includeDefs)
|
|
8860
|
-
|
|
11715
|
+
if (includeDefs) {
|
|
11716
|
+
visitDef(node.id, node);
|
|
11717
|
+
}
|
|
8861
11718
|
return ["body", "superClass"];
|
|
8862
11719
|
case "FunctionDeclaration":
|
|
8863
|
-
if (includeDefs)
|
|
8864
|
-
|
|
11720
|
+
if (includeDefs) {
|
|
11721
|
+
visitDef(node.id, node);
|
|
11722
|
+
}
|
|
8865
11723
|
return ["params", "returnType", "body"];
|
|
8866
11724
|
case "TypedefDeclaration":
|
|
8867
|
-
if (includeDefs)
|
|
8868
|
-
|
|
11725
|
+
if (includeDefs) {
|
|
11726
|
+
visitDef(node.id, node, "type_decls");
|
|
11727
|
+
}
|
|
8869
11728
|
return ["ts"];
|
|
8870
11729
|
case "VariableDeclarator":
|
|
8871
|
-
if (includeDefs)
|
|
8872
|
-
|
|
11730
|
+
if (includeDefs) {
|
|
11731
|
+
visitDef(node.id, node);
|
|
11732
|
+
}
|
|
11733
|
+
if (node.id.type === "BinaryExpression") {
|
|
11734
|
+
state.traverse(node.id.right);
|
|
11735
|
+
}
|
|
8873
11736
|
return ["init"];
|
|
8874
11737
|
case "EnumDeclaration":
|
|
8875
|
-
if (includeDefs)
|
|
11738
|
+
if (includeDefs) {
|
|
11739
|
+
if (node.id) {
|
|
11740
|
+
visitDef(node.id, node, "type_decls");
|
|
11741
|
+
}
|
|
8876
11742
|
break;
|
|
11743
|
+
}
|
|
8877
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
|
+
}
|
|
8878
11751
|
case "CatchClause":
|
|
8879
11752
|
if (includeDefs)
|
|
8880
11753
|
break;
|
|
@@ -8901,6 +11774,9 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
|
|
|
8901
11774
|
|
|
8902
11775
|
|
|
8903
11776
|
|
|
11777
|
+
|
|
11778
|
+
|
|
11779
|
+
|
|
8904
11780
|
/*
|
|
8905
11781
|
* This is an unfortunate hack. I want to be able to extract things
|
|
8906
11782
|
* like the types of all of a Class's variables (in particular the type
|
|
@@ -9105,6 +11981,19 @@ function sameLookupDefinition(a, b) {
|
|
|
9105
11981
|
function sameLookupResult(a, b) {
|
|
9106
11982
|
return (0,external_util_cjs_.sameArrays)(a, b, sameLookupDefinition);
|
|
9107
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
|
+
}
|
|
9108
11997
|
function isLookupCandidate(node) {
|
|
9109
11998
|
return node.computed
|
|
9110
11999
|
? node.property.type === "UnaryExpression" &&
|
|
@@ -9181,26 +12070,26 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
|
|
|
9181
12070
|
}
|
|
9182
12071
|
case "ThisExpression": {
|
|
9183
12072
|
for (let i = stack.length;;) {
|
|
9184
|
-
const si = stack[--i];
|
|
12073
|
+
const si = stack[--i].sn;
|
|
9185
12074
|
if (si.type === "ModuleDeclaration" ||
|
|
9186
12075
|
si.type === "ClassDeclaration" ||
|
|
9187
12076
|
!i) {
|
|
9188
12077
|
return [
|
|
9189
12078
|
name || si.name,
|
|
9190
|
-
[{ parent: i ? stack[i - 1] : null, results: [si] }],
|
|
12079
|
+
[{ parent: i ? stack[i - 1].sn : null, results: [si] }],
|
|
9191
12080
|
];
|
|
9192
12081
|
}
|
|
9193
12082
|
}
|
|
9194
12083
|
}
|
|
9195
12084
|
case "Identifier": {
|
|
9196
12085
|
if (node.name === "$") {
|
|
9197
|
-
return [name || node.name, [{ parent: null, results: [stack[0]] }]];
|
|
12086
|
+
return [name || node.name, [{ parent: null, results: [stack[0].sn] }]];
|
|
9198
12087
|
}
|
|
9199
12088
|
let inStatic = false;
|
|
9200
12089
|
let checkedImports = ignoreImports;
|
|
9201
12090
|
let imports = null;
|
|
9202
12091
|
for (let i = stack.length; i--;) {
|
|
9203
|
-
const si = stack[i];
|
|
12092
|
+
const si = stack[i].sn;
|
|
9204
12093
|
switch (si.type) {
|
|
9205
12094
|
case "ClassDeclaration":
|
|
9206
12095
|
if (inStatic && state.config?.enforceStatic !== "NO") {
|
|
@@ -9297,6 +12186,26 @@ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
|
|
|
9297
12186
|
}
|
|
9298
12187
|
return [false, false];
|
|
9299
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
|
+
}
|
|
9300
12209
|
function stateFuncs() {
|
|
9301
12210
|
let currentEnum = null;
|
|
9302
12211
|
return {
|
|
@@ -9334,9 +12243,10 @@ function stateFuncs() {
|
|
|
9334
12243
|
return lookup(this, "type_decls", node, name, stack);
|
|
9335
12244
|
},
|
|
9336
12245
|
stackClone() {
|
|
9337
|
-
return this.stack.
|
|
9338
|
-
|
|
9339
|
-
|
|
12246
|
+
return this.stack.slice();
|
|
12247
|
+
},
|
|
12248
|
+
top() {
|
|
12249
|
+
return this.stack[this.stack.length - 1];
|
|
9340
12250
|
},
|
|
9341
12251
|
traverse(root) {
|
|
9342
12252
|
return (0,src_ast/* traverseAst */.lA)(root, (node) => {
|
|
@@ -9363,7 +12273,7 @@ function stateFuncs() {
|
|
|
9363
12273
|
if (this.stack.length !== 1) {
|
|
9364
12274
|
throw new Error("Unexpected stack length for Program node");
|
|
9365
12275
|
}
|
|
9366
|
-
this.stack[0].node = node;
|
|
12276
|
+
this.stack[0].sn.node = node;
|
|
9367
12277
|
break;
|
|
9368
12278
|
case "TypeSpecList":
|
|
9369
12279
|
case "TypeSpecPart":
|
|
@@ -9371,10 +12281,9 @@ function stateFuncs() {
|
|
|
9371
12281
|
break;
|
|
9372
12282
|
case "ImportModule":
|
|
9373
12283
|
case "Using": {
|
|
9374
|
-
const
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
}
|
|
12284
|
+
const parent = { ...this.stack.pop() };
|
|
12285
|
+
this.stack.push(parent);
|
|
12286
|
+
parent.usings = parent.usings ? { ...parent.usings } : {};
|
|
9378
12287
|
const name = (node.type === "Using" && node.as && node.as.name) ||
|
|
9379
12288
|
(node.id.type === "Identifier"
|
|
9380
12289
|
? node.id.name
|
|
@@ -9386,6 +12295,7 @@ function stateFuncs() {
|
|
|
9386
12295
|
parent.imports = [using];
|
|
9387
12296
|
}
|
|
9388
12297
|
else {
|
|
12298
|
+
parent.imports = parent.imports.slice();
|
|
9389
12299
|
const index = parent.imports.findIndex((using) => (using.node.id.type === "Identifier"
|
|
9390
12300
|
? using.node.id.name
|
|
9391
12301
|
: using.node.id.property.name) === name);
|
|
@@ -9398,35 +12308,39 @@ function stateFuncs() {
|
|
|
9398
12308
|
}
|
|
9399
12309
|
case "CatchClause":
|
|
9400
12310
|
if (node.param) {
|
|
9401
|
-
const
|
|
12311
|
+
const parent = this.top().sn;
|
|
9402
12312
|
if (!parent.decls)
|
|
9403
12313
|
parent.decls = {};
|
|
9404
12314
|
const id = node.param.type === "Identifier"
|
|
9405
12315
|
? node.param
|
|
9406
12316
|
: node.param.left;
|
|
9407
12317
|
this.stack.push({
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
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
|
+
},
|
|
9414
12326
|
});
|
|
9415
12327
|
}
|
|
9416
12328
|
break;
|
|
9417
12329
|
case "ForStatement":
|
|
9418
12330
|
if (node.init && node.init.type === "VariableDeclaration") {
|
|
9419
12331
|
this.stack.push({
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
12332
|
+
sn: {
|
|
12333
|
+
type: "BlockStatement",
|
|
12334
|
+
fullName: this.top().sn.fullName,
|
|
12335
|
+
name: undefined,
|
|
12336
|
+
node: node,
|
|
12337
|
+
attributes: 0,
|
|
12338
|
+
},
|
|
9425
12339
|
});
|
|
9426
12340
|
}
|
|
9427
12341
|
break;
|
|
9428
12342
|
case "BlockStatement": {
|
|
9429
|
-
const
|
|
12343
|
+
const parent = this.top().sn;
|
|
9430
12344
|
if (parent.node === node ||
|
|
9431
12345
|
(parent.type !== "FunctionDeclaration" &&
|
|
9432
12346
|
parent.type !== "BlockStatement")) {
|
|
@@ -9437,10 +12351,10 @@ function stateFuncs() {
|
|
|
9437
12351
|
case "ClassDeclaration":
|
|
9438
12352
|
case "FunctionDeclaration":
|
|
9439
12353
|
case "ModuleDeclaration": {
|
|
9440
|
-
const
|
|
12354
|
+
const parent = this.top().sn;
|
|
9441
12355
|
const name = "id" in node ? node.id && node.id.name : undefined;
|
|
9442
12356
|
const fullName = this.stack
|
|
9443
|
-
.map((e) => e.name)
|
|
12357
|
+
.map((e) => e.sn.name)
|
|
9444
12358
|
.concat(name)
|
|
9445
12359
|
.filter((e) => e != null)
|
|
9446
12360
|
.join(".");
|
|
@@ -9453,7 +12367,7 @@ function stateFuncs() {
|
|
|
9453
12367
|
? 0
|
|
9454
12368
|
: stateNodeAttrs(node.attrs),
|
|
9455
12369
|
};
|
|
9456
|
-
this.stack.push(elm);
|
|
12370
|
+
this.stack.push({ sn: elm });
|
|
9457
12371
|
if (name) {
|
|
9458
12372
|
if (!parent.decls)
|
|
9459
12373
|
parent.decls = {};
|
|
@@ -9462,7 +12376,7 @@ function stateFuncs() {
|
|
|
9462
12376
|
const e = parent.decls[name].find((d) => isStateNode(d) && d[what] === elm[what]);
|
|
9463
12377
|
if (e != null) {
|
|
9464
12378
|
e.node = node;
|
|
9465
|
-
this.
|
|
12379
|
+
this.top().sn = e;
|
|
9466
12380
|
break;
|
|
9467
12381
|
}
|
|
9468
12382
|
}
|
|
@@ -9496,7 +12410,7 @@ function stateFuncs() {
|
|
|
9496
12410
|
this.inType++;
|
|
9497
12411
|
break;
|
|
9498
12412
|
}
|
|
9499
|
-
const
|
|
12413
|
+
const parent = this.top().sn;
|
|
9500
12414
|
const name = (parent.fullName + "." + node.id.name).replace(/^\$\./, "");
|
|
9501
12415
|
node.body.members.forEach((m) => (("init" in m ? m.init : m).enumType = name));
|
|
9502
12416
|
}
|
|
@@ -9504,7 +12418,7 @@ function stateFuncs() {
|
|
|
9504
12418
|
case "TypedefDeclaration": {
|
|
9505
12419
|
this.inType++;
|
|
9506
12420
|
const name = node.id.name;
|
|
9507
|
-
const
|
|
12421
|
+
const parent = this.top().sn;
|
|
9508
12422
|
if (!parent.type_decls)
|
|
9509
12423
|
parent.type_decls = {};
|
|
9510
12424
|
if (!(0,src_ast/* hasProperty */.l$)(parent.type_decls, name)) {
|
|
@@ -9528,7 +12442,7 @@ function stateFuncs() {
|
|
|
9528
12442
|
break;
|
|
9529
12443
|
}
|
|
9530
12444
|
case "VariableDeclaration": {
|
|
9531
|
-
const
|
|
12445
|
+
const parent = this.top().sn;
|
|
9532
12446
|
if (!parent.decls)
|
|
9533
12447
|
parent.decls = {};
|
|
9534
12448
|
const decls = parent.decls;
|
|
@@ -9564,7 +12478,7 @@ function stateFuncs() {
|
|
|
9564
12478
|
throw new Error(`Expected inType to be 1 at EnumStringBody. Got ${this.inType}.`);
|
|
9565
12479
|
}
|
|
9566
12480
|
this.inType--;
|
|
9567
|
-
const
|
|
12481
|
+
const parent = this.top().sn;
|
|
9568
12482
|
const values = parent.decls || (parent.decls = {});
|
|
9569
12483
|
let prev = -1;
|
|
9570
12484
|
node.members.forEach((m, i) => {
|
|
@@ -9656,17 +12570,19 @@ function stateFuncs() {
|
|
|
9656
12570
|
this.inType++;
|
|
9657
12571
|
break;
|
|
9658
12572
|
}
|
|
9659
|
-
const
|
|
9660
|
-
if (parent.node === node ||
|
|
12573
|
+
const parent = this.top();
|
|
12574
|
+
if (parent.sn.node === node ||
|
|
9661
12575
|
// The pre function might cause node.body to be skipped,
|
|
9662
12576
|
// so we need to check here, just in case.
|
|
9663
12577
|
// (this actually happens with prettier-extenison-monkeyc's
|
|
9664
12578
|
// findItemsByRange)
|
|
9665
|
-
(node.type === "CatchClause" && parent.node === node.body)) {
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
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);
|
|
9670
12586
|
}
|
|
9671
12587
|
}
|
|
9672
12588
|
}
|
|
@@ -9692,11 +12608,13 @@ function collectNamespaces(ast, stateIn) {
|
|
|
9692
12608
|
if (!state.stack) {
|
|
9693
12609
|
state.stack = [
|
|
9694
12610
|
{
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
12611
|
+
sn: {
|
|
12612
|
+
type: "Program",
|
|
12613
|
+
name: "$",
|
|
12614
|
+
fullName: "$",
|
|
12615
|
+
node: undefined,
|
|
12616
|
+
attributes: 0,
|
|
12617
|
+
},
|
|
9700
12618
|
},
|
|
9701
12619
|
];
|
|
9702
12620
|
}
|
|
@@ -9721,10 +12639,10 @@ function collectNamespaces(ast, stateIn) {
|
|
|
9721
12639
|
if (state.stack.length !== 1) {
|
|
9722
12640
|
throw new Error("Invalid AST!");
|
|
9723
12641
|
}
|
|
9724
|
-
if (state.stack[0].type !== "Program") {
|
|
12642
|
+
if (state.stack[0].sn.type !== "Program") {
|
|
9725
12643
|
throw new Error("Bottom of stack was not a Program!");
|
|
9726
12644
|
}
|
|
9727
|
-
return state.stack[0];
|
|
12645
|
+
return state.stack[0].sn;
|
|
9728
12646
|
}
|
|
9729
12647
|
function formatAst(node, monkeyCSource = null, options = null) {
|
|
9730
12648
|
/*
|
|
@@ -9763,10 +12681,152 @@ function formatAst(node, monkeyCSource = null, options = null) {
|
|
|
9763
12681
|
endOfLine: "lf",
|
|
9764
12682
|
});
|
|
9765
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
|
+
}
|
|
9766
12826
|
function handleException(state, node, exception) {
|
|
9767
12827
|
try {
|
|
9768
12828
|
const fullName = state.stack
|
|
9769
|
-
.map((e) => e.name)
|
|
12829
|
+
.map((e) => e.sn.name)
|
|
9770
12830
|
.concat("name" in node && typeof node.name === "string" ? node.name : undefined)
|
|
9771
12831
|
.filter((e) => e != null)
|
|
9772
12832
|
.join(".");
|
|
@@ -9791,7 +12851,7 @@ function handleException(state, node, exception) {
|
|
|
9791
12851
|
function findUsing(state, stack, using) {
|
|
9792
12852
|
if (using.module)
|
|
9793
12853
|
return using.module;
|
|
9794
|
-
let module = stack[0];
|
|
12854
|
+
let module = stack[0].sn;
|
|
9795
12855
|
const find = (node) => {
|
|
9796
12856
|
let name;
|
|
9797
12857
|
if (node.type === "Identifier") {
|
|
@@ -9839,7 +12899,7 @@ function findUsingForNode(state, stack, i, node) {
|
|
|
9839
12899
|
imports = [];
|
|
9840
12900
|
imports.push({
|
|
9841
12901
|
name: `${module.fullName}.${node.name}`,
|
|
9842
|
-
decls: { parent: si, results: module.type_decls[node.name] },
|
|
12902
|
+
decls: { parent: si.sn, results: module.type_decls[node.name] },
|
|
9843
12903
|
});
|
|
9844
12904
|
}
|
|
9845
12905
|
}
|
|
@@ -9861,7 +12921,7 @@ function getApiFunctionInfo(state, func) {
|
|
|
9861
12921
|
};
|
|
9862
12922
|
}
|
|
9863
12923
|
if (func.name === "initialize") {
|
|
9864
|
-
const top = func.stack[func.stack.length - 1];
|
|
12924
|
+
const top = func.stack[func.stack.length - 1].sn;
|
|
9865
12925
|
if (top.type === "ClassDeclaration") {
|
|
9866
12926
|
top.hasInvoke = true;
|
|
9867
12927
|
}
|
|
@@ -9874,7 +12934,7 @@ function markInvokeClassMethod(state, func) {
|
|
|
9874
12934
|
func.info = state.invokeInfo;
|
|
9875
12935
|
}
|
|
9876
12936
|
function isLocal(v) {
|
|
9877
|
-
return v.stack[v.stack.length - 1]?.type === "BlockStatement";
|
|
12937
|
+
return v.stack[v.stack.length - 1]?.sn.type === "BlockStatement";
|
|
9878
12938
|
}
|
|
9879
12939
|
function diagnostic(state, node, message, type = "INFO", extra) {
|
|
9880
12940
|
if (!state.diagnostics)
|