@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/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.stack[state.stack.length - 1];
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 _optimizer_types__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9697);
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 stack = func.attributes & StateNodeAttributes.STATIC
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, stack);
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 callerSn = state.stack.find((sn) => sn.type === "FunctionDeclaration");
2582
- if (!callerSn) {
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
- /***/ 4499:
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 */ "k": () => (/* binding */ negativeFixups)
2708
+ /* harmony export */ "BJ": () => (/* binding */ analyze)
2712
2709
  /* harmony export */ });
2713
- /*
2714
- * This is strange. It pretty much has to be a bug in the sdk,
2715
- * but its the same in every sdk.
2716
- *
2717
- * ${sdk}/bin/api.mir describes the Toybox api down to every module,
2718
- * class, function, enum and constant, together with the enum
2719
- * and constant values.
2720
- *
2721
- * The problem is that every enum or constant with a negative
2722
- * value, appears with the corresponding positive value in api.mir.
2723
- * So eg Graphics.COLOR_TRANSPARENT should be -1, but instead, it has
2724
- * the value 1.
2725
- *
2726
- * This is a (currently somewhat ad-hoc) list of the negative constants
2727
- * so we can fix them after reading api.mir.
2728
- */
2729
- const negativeFixups = [
2730
- "Toybox.Communications.BLE_CONNECTION_UNAVAILABLE",
2731
- "Toybox.Communications.INVALID_HTTP_BODY_IN_REQUEST",
2732
- "Toybox.Communications.REQUEST_CANCELLED",
2733
- "Toybox.Communications.UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE",
2734
- "Toybox.Communications.UNABLE_TO_PROCESS_IMAGE",
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
- "use strict";
2775
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2776
- /* harmony export */ "m": () => (/* binding */ StateNodeAttributes)
2777
- /* harmony export */ });
2778
- var StateNodeAttributes;
2779
- (function (StateNodeAttributes) {
2780
- StateNodeAttributes[StateNodeAttributes["PUBLIC"] = 1] = "PUBLIC";
2781
- StateNodeAttributes[StateNodeAttributes["PROTECTED"] = 2] = "PROTECTED";
2782
- StateNodeAttributes[StateNodeAttributes["PRIVATE"] = 4] = "PRIVATE";
2783
- StateNodeAttributes[StateNodeAttributes["STATIC"] = 8] = "STATIC";
2784
- })(StateNodeAttributes || (StateNodeAttributes = {}));
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
- blockState.set(decl, { ...v, curType: value });
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 & SingleTonTypeTagsConst &&
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 & ~SingleTonTypeTagsConst;
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.stack[istate.state.stack.length - 1];
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 restrictByEqualityByComponent(a, b) {
6468
- let bits = a.type;
6469
- if (a.value == null && (b.type & bits) === b.type) {
6470
- // shortcut:
6471
- // if b.type is contained in a.type, and a has no
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
- let br = null;
6476
- do {
6477
- const next = bits & (bits - 1);
6478
- const bit = bits - next;
6479
- const brt = restrictExactTypesByEquality({
6480
- type: bit,
6481
- value: getUnionComponent(a, bit) || undefined,
6482
- }, b);
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
- else {
6488
- unionInto(br, brt);
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 br;
9031
+ return tryIdentity(istate, node, left, right);
6497
9032
  }
6498
9033
  /*
6499
- * Given that a == b, reutnr what we can deduce about b's
6500
- * type.
9034
+ * Try to reorder (a op K1) op K2 => a op (K1 op K2),
9035
+ * and fold K1 op K2.
6501
9036
  *
6502
- * Note that this is similar to intersection. In many cases, it
6503
- * is intersection. eg if a is the type (Null or "Hello" or Menu)
6504
- * then we know b's type must be a subtype of intersection(a, b).
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
- * But eg if it was a == 5, and a is known to be Char, String,
6507
- * Number or Float, we can only eliminate String, because
6508
- * 5.0 == 5, and 5.toChar() == 5.
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 restrictByEquality(a, b) {
6511
- if (a.type === 0 /* TypeTag.Never */)
6512
- return a;
6513
- if (isExact(a)) {
6514
- return restrictExactTypesByEquality(a, b);
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
- return restrictByEqualityByComponent(a, b);
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, SingleTonTypeTagsConst, UnionDataTypeTagsConst, ValueTypeTagsConst, ObjectLikeTagsConst, EnumTagsConst, isExact, isUnion, isSingleton, hasValue, hasNoData, lookupByFullName, cloneType, typeFromTypeStateNode, typeFromTypeStateNodes, typeFromSingleTypeSpec, typeFromTypespec, typeFromLiteral, mcExprFromType, castType, TruthyTypes, mustBeTrue, mustBeFalse, display, hasUnionData, getObjectValue, forEachUnionComponent, getUnionComponent, setUnionComponent */
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 SingleTonTypeTagsConst = 1 /* TypeTag.Null */ | 2 /* TypeTag.False */ | 4 /* TypeTag.True */;
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 = SingleTonTypeTagsConst | (ValueTypeTagsConst & ~131072 /* TypeTag.Symbol */);
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 & SingleTonTypeTagsConst) !== 0;
9522
+ return isExact(v) && (v.type & SingletonTypeTagsConst) !== 0;
6870
9523
  }
6871
9524
  function hasValue(v) {
6872
9525
  return (isExact(v) &&
6873
- ((v.type & SingleTonTypeTagsConst) !== 0 || v.value !== undefined));
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 & ~SingleTonTypeTagsConst) === 0);
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 &= ~SingleTonTypeTagsConst;
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 & ~SingleTonTypeTagsConst;
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 & ~SingleTonTypeTagsConst)) {
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 &= ~SingleTonTypeTagsConst;
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_.sameLookupResult)(results, defn)) {
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
- break;
11710
+ if (includeDefs) {
11711
+ visitDef(node.id);
11712
+ }
8857
11713
  return ["body"];
8858
11714
  case "ClassDeclaration":
8859
- if (includeDefs)
8860
- break;
11715
+ if (includeDefs) {
11716
+ visitDef(node.id, node);
11717
+ }
8861
11718
  return ["body", "superClass"];
8862
11719
  case "FunctionDeclaration":
8863
- if (includeDefs)
8864
- break;
11720
+ if (includeDefs) {
11721
+ visitDef(node.id, node);
11722
+ }
8865
11723
  return ["params", "returnType", "body"];
8866
11724
  case "TypedefDeclaration":
8867
- if (includeDefs)
8868
- break;
11725
+ if (includeDefs) {
11726
+ visitDef(node.id, node, "type_decls");
11727
+ }
8869
11728
  return ["ts"];
8870
11729
  case "VariableDeclarator":
8871
- if (includeDefs)
8872
- break;
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.map((elm) => elm.type === "ModuleDeclaration" || elm.type === "Program"
9338
- ? { ...elm }
9339
- : elm);
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 [parent] = this.stack.slice(-1);
9375
- if (!parent.usings) {
9376
- parent.usings = {};
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 [parent] = this.stack.slice(-1);
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
- type: "BlockStatement",
9409
- fullName: parent.fullName,
9410
- name: undefined,
9411
- node: node.body,
9412
- decls: { [id.name]: [id] },
9413
- attributes: 0,
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
- type: "BlockStatement",
9421
- fullName: this.stack.slice(-1).pop().fullName,
9422
- name: undefined,
9423
- node: node,
9424
- attributes: 0,
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 [parent] = this.stack.slice(-1);
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 [parent] = this.stack.slice(-1);
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.stack.splice(-1, 1, e);
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 [parent] = this.stack.slice(-1);
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 [parent] = this.stack.slice(-1);
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 [parent] = this.stack.slice(-1);
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 [parent] = this.stack.slice(-1);
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 [parent] = this.stack.slice(-1);
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
- delete parent.usings;
9667
- delete parent.imports;
9668
- if (node.type !== "Program") {
9669
- this.stack.pop();
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
- type: "Program",
9696
- name: "$",
9697
- fullName: "$",
9698
- node: undefined,
9699
- attributes: 0,
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)