@markw65/monkeyc-optimizer 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -636,3 +636,16 @@ Bug Fixes
636
636
  - Code cleanup
637
637
  - refactor some of the type code for better type safety
638
638
  - turn on the eslint rule eqeqeq and fix all the issues
639
+
640
+ ### 1.1.5
641
+
642
+ - Bug fixes
643
+ - Always evaluate a constant's initializer to determine its type
644
+ - Fix a bug refining the object type based on the properties it accesses that could lose the type of the object.
645
+
646
+ ### 1.1.6
647
+
648
+ - Bug fixes
649
+ - Fix an issue in restrictByEquality when restricting a union including an Enum, to a specific value of the enum.
650
+ - Fix the display of Method types to match the syntax used in MonkeyC.
651
+ - Infer the type of method(:symbol) by looking up symbol.
package/build/api.cjs CHANGED
@@ -3022,45 +3022,115 @@ function printBlockState(block, state, indent = "") {
3022
3022
  console.log(`${indent} - ${typeStateEntry(value, key)}${value.equivSet ? " " + tsEquivs(state, key) : ""}`);
3023
3023
  });
3024
3024
  }
3025
- function filterDecls(decls, possible) {
3025
+ /*
3026
+ * We have an object, and a MemberExpression object.<name>
3027
+ * - decls are the StateNodes associated with the known type
3028
+ * of object.
3029
+ * - possible are all the StateNodes that declare <name>
3030
+ *
3031
+ * We want to find all the elements of possible which are
3032
+ * "compatible" with decls, which tells us the set of things
3033
+ * that object.<name> could correspond to, and also what that
3034
+ * tells us about object.
3035
+ *
3036
+ * The return value is two arrays of StateNode. The first
3037
+ * gives the refined type of object, and the second is the
3038
+ * array of StateNodes that could declare <name>
3039
+ */
3040
+ function filterDecls(decls, possible, name) {
3026
3041
  if (!possible)
3027
- return null;
3028
- return decls.reduce((cur, decl) => {
3042
+ return [null, null];
3043
+ const result = decls.reduce((cur, decl) => {
3029
3044
  const found = possible.reduce((flag, poss) => {
3030
3045
  if (decl === poss ||
3031
- (poss.type === "ClassDeclaration" &&
3032
- (0,_api__WEBPACK_IMPORTED_MODULE_0__.getSuperClasses)(poss)?.has(decl)) ||
3033
- (decl.type === "ClassDeclaration" && (0,_api__WEBPACK_IMPORTED_MODULE_0__.getSuperClasses)(decl)?.has(poss))) {
3034
- if (!cur)
3035
- cur = [poss];
3036
- else
3037
- cur.push(poss);
3046
+ (poss.type === "ClassDeclaration" && (0,_api__WEBPACK_IMPORTED_MODULE_0__.getSuperClasses)(poss)?.has(decl))) {
3047
+ // poss extends decl, so decl must actually be a poss
3048
+ // eg we know obj is an Object, and we call obj.toNumber
3049
+ // so possible includes all the classes that declare toNumber
3050
+ // so we can refine obj's type to the union of those types
3051
+ if (!cur[0]) {
3052
+ cur = [new Set(), new Set()];
3053
+ }
3054
+ cur[0].add(poss);
3055
+ cur[1].add(poss);
3056
+ return true;
3057
+ }
3058
+ else if (decl.type === "ClassDeclaration" &&
3059
+ (0,_api__WEBPACK_IMPORTED_MODULE_0__.getSuperClasses)(decl)?.has(poss)) {
3060
+ // decl extends poss, so decl remains unchanged
3061
+ // eg we know obj is Menu2, we call obj.toString
3062
+ // Menu2 doesn't define toString, but Object does
3063
+ // so poss is Object. But we still know that
3064
+ // obj is Menu2
3065
+ if (!cur[0]) {
3066
+ cur = [new Set(), new Set()];
3067
+ }
3068
+ cur[0].add(decl);
3069
+ cur[1].add(poss);
3038
3070
  return true;
3039
3071
  }
3040
3072
  return flag;
3041
3073
  }, false);
3042
3074
  if (!found) {
3043
- possible.some((poss) => {
3044
- if (decl.stack?.some((sn) => sn.decls === poss.decls)) {
3045
- if (!cur)
3046
- cur = [poss];
3047
- else
3048
- cur.push(poss);
3049
- return true;
3050
- }
3051
- return false;
3052
- });
3075
+ // If we didn't find the property in any of the
3076
+ // standard places, the runtime might still find
3077
+ // it by searching up the Module stack (and up
3078
+ // the module stack from any super classes)
3079
+ //
3080
+ // eg
3081
+ //
3082
+ // obj = Application.getApp();
3083
+ // obj.Properties.whatever
3084
+ //
3085
+ // Properties doesn't exist on AppBase, but AppBase
3086
+ // is declared in Application, and Application
3087
+ // does declare Properties. So Application.Properties
3088
+ // is (one of) the declarations we should find; but we
3089
+ // must not refine obj's type to include Application.
3090
+ let d = [decl];
3091
+ do {
3092
+ d.forEach((d) => {
3093
+ const stack = d.stack;
3094
+ possible.forEach((poss) => {
3095
+ for (let i = stack.length; i--;) {
3096
+ const sn = stack[i];
3097
+ if (sn.decls === poss.decls) {
3098
+ if (!cur[0]) {
3099
+ cur = [new Set(), new Set()];
3100
+ }
3101
+ cur[0].add(decl);
3102
+ cur[1].add(poss);
3103
+ break;
3104
+ }
3105
+ if ((0,_api__WEBPACK_IMPORTED_MODULE_0__.hasProperty)(sn.decls, name)) {
3106
+ break;
3107
+ }
3108
+ }
3109
+ });
3110
+ });
3111
+ d = d.flatMap((d) => {
3112
+ if (d.type !== "ClassDeclaration" ||
3113
+ !d.superClass ||
3114
+ d.superClass === true) {
3115
+ return [];
3116
+ }
3117
+ return d.superClass;
3118
+ });
3119
+ } while (d.length);
3053
3120
  }
3054
3121
  return cur;
3055
- }, null);
3122
+ }, [null, null]);
3123
+ if (!result[0])
3124
+ return [null, null];
3125
+ return [Array.from(result[0]), Array.from(result[1])];
3056
3126
  }
3057
3127
  function findObjectDeclsByProperty(state, object, next) {
3058
3128
  const decls = (0,_type_flow_types__WEBPACK_IMPORTED_MODULE_11__/* .getStateNodeDeclsFromType */ .iX)(state, object);
3059
3129
  if (!decls)
3060
- return null;
3130
+ return [null, null];
3061
3131
  const possibleDecls = (0,_api__WEBPACK_IMPORTED_MODULE_0__.hasProperty)(state.allDeclarations, next.property.name) &&
3062
3132
  state.allDeclarations[next.property.name];
3063
- return filterDecls(decls, possibleDecls);
3133
+ return filterDecls(decls, possibleDecls, next.property.name);
3064
3134
  }
3065
3135
  function refineObjectTypeByDecls(istate, object, trueDecls) {
3066
3136
  const refinedType = typeFromTypeStateNodes(istate.state, trueDecls);
@@ -3076,13 +3146,13 @@ function findNextObjectType(istate, trueDecls, next) {
3076
3146
  }, { type: 0 /* TypeTag.Never */ });
3077
3147
  }
3078
3148
  function resolveDottedMember(istate, object, next) {
3079
- const decls = findObjectDeclsByProperty(istate.state, object, next);
3080
- if (!decls)
3149
+ const [objDecls, trueDecls] = findObjectDeclsByProperty(istate.state, object, next);
3150
+ if (!objDecls)
3081
3151
  return null;
3082
- const property = findNextObjectType(istate, decls, next);
3152
+ const property = findNextObjectType(istate, trueDecls, next);
3083
3153
  if (!property)
3084
3154
  return null;
3085
- const type = refineObjectTypeByDecls(istate, object, decls);
3155
+ const type = refineObjectTypeByDecls(istate, object, objDecls);
3086
3156
  const mayThrow = !subtypeOf(object, type);
3087
3157
  return { mayThrow, object: type, property };
3088
3158
  }
@@ -3119,11 +3189,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
3119
3189
  next = value.obj[me.property.name];
3120
3190
  }
3121
3191
  else {
3122
- const trueDecls = findObjectDeclsByProperty(istate.state, cur, me);
3123
- if (!trueDecls) {
3192
+ const [objDecls, trueDecls] = findObjectDeclsByProperty(istate.state, cur, me);
3193
+ if (!objDecls) {
3124
3194
  return null;
3125
3195
  }
3126
- cur = refineObjectTypeByDecls(istate, cur, trueDecls);
3196
+ cur = refineObjectTypeByDecls(istate, cur, objDecls);
3127
3197
  next = findNextObjectType(istate, trueDecls, me);
3128
3198
  }
3129
3199
  }
@@ -3310,7 +3380,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
3310
3380
  const info = sysCallInfo(callee);
3311
3381
  if (!info)
3312
3382
  return false;
3313
- const result = info(callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
3383
+ const result = info(istate.state, callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
3314
3384
  if (result.calleeObj) {
3315
3385
  setStateEvent(curState, calleeObjDecl, result.calleeObj, false);
3316
3386
  }
@@ -4632,14 +4702,18 @@ function evaluateLogicalTypes(op, left, right) {
4632
4702
 
4633
4703
  "use strict";
4634
4704
  /* unused harmony exports evaluateCall, checkCallArgs, sysCallInfo */
4635
- /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6817);
4636
- /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_0__);
4637
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6906);
4638
- /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_1__);
4639
- /* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7161);
4640
- /* harmony import */ var _sub_type__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9234);
4641
- /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7255);
4642
- /* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(757);
4705
+ /* harmony import */ var _optimizer_types__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9697);
4706
+ /* harmony import */ var _type_flow__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4859);
4707
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6817);
4708
+ /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_api__WEBPACK_IMPORTED_MODULE_2__);
4709
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6906);
4710
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_3__);
4711
+ /* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7161);
4712
+ /* harmony import */ var _sub_type__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9234);
4713
+ /* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7255);
4714
+ /* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(757);
4715
+
4716
+
4643
4717
 
4644
4718
 
4645
4719
 
@@ -4668,6 +4742,20 @@ function evaluateCall(istate, node, callee, args) {
4668
4742
  }
4669
4743
  return checkCallArgs(istate, node, callee.value, args);
4670
4744
  }
4745
+ function calleeObjectType(istate, callee) {
4746
+ if (callee.type === "MemberExpression") {
4747
+ return (istate.typeMap?.get(callee.object) || {
4748
+ type: 524287 /* TypeTag.Any */,
4749
+ });
4750
+ }
4751
+ if (callee.type === "Identifier" && istate.func) {
4752
+ const func = istate.func;
4753
+ const [self] = func.stack.slice(-1);
4754
+ return typeFromTypeStateNode(istate.state, self, (func.attributes & StateNodeAttributes.STATIC) !== 0 ||
4755
+ self.type !== "ClassDeclaration");
4756
+ }
4757
+ return null;
4758
+ }
4671
4759
  function checkCallArgs(istate, node, callees, args) {
4672
4760
  const allDiags = [];
4673
4761
  const resultType = reduce(callees, (result, cur) => {
@@ -4677,13 +4765,11 @@ function checkCallArgs(istate, node, callees, args) {
4677
4765
  let returnType = null;
4678
4766
  let effects = true;
4679
4767
  let argEffects = true;
4680
- if (node.callee.type === "MemberExpression") {
4681
- const object = istate.typeMap?.get(node.callee.object) || {
4682
- type: 524287 /* TypeTag.Any */,
4683
- };
4768
+ const object = calleeObjectType(istate, node.callee);
4769
+ if (object) {
4684
4770
  const info = sysCallInfo(cur);
4685
4771
  if (info) {
4686
- const result = info(cur, object, () => args);
4772
+ const result = info(istate.state, cur, object, () => args);
4687
4773
  if (result.argTypes)
4688
4774
  argTypes = result.argTypes;
4689
4775
  if (result.returnType)
@@ -4822,7 +4908,7 @@ function sysCallInfo(func) {
4822
4908
  function getSystemCallTable() {
4823
4909
  if (systemCallInfo)
4824
4910
  return systemCallInfo;
4825
- const arrayAdd = (callee, calleeObj, getArgs) => {
4911
+ const arrayAdd = (state, callee, calleeObj, getArgs) => {
4826
4912
  const ret = {};
4827
4913
  if (calleeObj.type & 512 /* TypeTag.Array */) {
4828
4914
  const adata = getUnionComponent(calleeObj, 512 /* TypeTag.Array */);
@@ -4854,7 +4940,7 @@ function getSystemCallTable() {
4854
4940
  }
4855
4941
  return ret;
4856
4942
  };
4857
- const arrayRet = (callee, calleeObj, _getArgs) => {
4943
+ const arrayRet = (state, callee, calleeObj, _getArgs) => {
4858
4944
  const ret = { effectFree: true };
4859
4945
  if (calleeObj.type & 512 /* TypeTag.Array */) {
4860
4946
  const adata = getUnionComponent(calleeObj, 512 /* TypeTag.Array */);
@@ -4864,7 +4950,7 @@ function getSystemCallTable() {
4864
4950
  }
4865
4951
  return ret;
4866
4952
  };
4867
- const dictionaryGet = (callee, calleeObj, getArgs) => {
4953
+ const dictionaryGet = (state, callee, calleeObj, getArgs) => {
4868
4954
  const ret = { effectFree: true };
4869
4955
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
4870
4956
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -4878,7 +4964,7 @@ function getSystemCallTable() {
4878
4964
  }
4879
4965
  return ret;
4880
4966
  };
4881
- const dictionaryValues = (callee, calleeObj) => {
4967
+ const dictionaryValues = (state, callee, calleeObj) => {
4882
4968
  const ret = { effectFree: true };
4883
4969
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
4884
4970
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -4888,7 +4974,7 @@ function getSystemCallTable() {
4888
4974
  }
4889
4975
  return ret;
4890
4976
  };
4891
- const dictionaryKeys = (callee, calleeObj) => {
4977
+ const dictionaryKeys = (state, callee, calleeObj) => {
4892
4978
  const ret = { effectFree: true };
4893
4979
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
4894
4980
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -4898,7 +4984,7 @@ function getSystemCallTable() {
4898
4984
  }
4899
4985
  return ret;
4900
4986
  };
4901
- const dictionaryPut = (callee, calleeObj, getArgs) => {
4987
+ const dictionaryPut = (state, callee, calleeObj, getArgs) => {
4902
4988
  const ret = {};
4903
4989
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
4904
4990
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -4929,7 +5015,7 @@ function getSystemCallTable() {
4929
5015
  }
4930
5016
  return ret;
4931
5017
  };
4932
- const methodInvoke = (callee, calleeObj, getArgs) => {
5018
+ const methodInvoke = (state, callee, calleeObj, getArgs) => {
4933
5019
  const ret = { argEffects: true };
4934
5020
  if (calleeObj.type & 2048 /* TypeTag.Method */) {
4935
5021
  const data = getUnionComponent(calleeObj, 2048 /* TypeTag.Method */);
@@ -4942,9 +5028,49 @@ function getSystemCallTable() {
4942
5028
  ret.argTypes = getArgs();
4943
5029
  return ret;
4944
5030
  };
5031
+ const method = (state, callee, calleeObj, getArgs) => {
5032
+ const ret = {};
5033
+ const args = getArgs();
5034
+ if (args.length === 1 &&
5035
+ hasValue(args[0]) &&
5036
+ args[0].type === 131072 /* TypeTag.Symbol */) {
5037
+ const symbol = {
5038
+ type: "Identifier",
5039
+ name: args[0].value,
5040
+ };
5041
+ const next = {
5042
+ type: "MemberExpression",
5043
+ object: symbol,
5044
+ property: symbol,
5045
+ computed: false,
5046
+ };
5047
+ const [, trueDecls] = findObjectDeclsByProperty(state, calleeObj, next);
5048
+ if (!trueDecls)
5049
+ return ret;
5050
+ const callees = trueDecls
5051
+ .flatMap((decl) => decl.decls?.[symbol.name])
5052
+ .filter((decl) => decl?.type === "FunctionDeclaration");
5053
+ if (!callees.length)
5054
+ return ret;
5055
+ ret.returnType = callees.reduce((type, callee) => {
5056
+ const result = callee.node.returnType
5057
+ ? typeFromTypespec(state, callee.node.returnType.argument, callee.stack)
5058
+ : { type: 524287 /* TypeTag.Any */ };
5059
+ const args = callee.node.params.map((param) => param.type === "BinaryExpression"
5060
+ ? typeFromTypespec(state, param.right, callee.stack)
5061
+ : { type: 524287 /* TypeTag.Any */ });
5062
+ unionInto(type, {
5063
+ type: 2048 /* TypeTag.Method */,
5064
+ value: { result, args },
5065
+ });
5066
+ return type;
5067
+ }, { type: 0 /* TypeTag.Never */ });
5068
+ }
5069
+ return ret;
5070
+ };
4945
5071
  const nop = () => ({ effectFree: true });
4946
5072
  const mod = () => ({});
4947
- const rounder = (callee, calleeObj, getArgs) => {
5073
+ const rounder = (state, callee, calleeObj, getArgs) => {
4948
5074
  const results = {};
4949
5075
  const fn = Math[callee.name];
4950
5076
  results.effectFree = true;
@@ -4962,7 +5088,7 @@ function getSystemCallTable() {
4962
5088
  : { type: 120 /* TypeTag.Numeric */ };
4963
5089
  return results;
4964
5090
  };
4965
- const mathHelper = (callee, calleeObj, getArgs, helper) => {
5091
+ const mathHelper = (state, callee, calleeObj, getArgs, helper) => {
4966
5092
  const results = {};
4967
5093
  const fn = helper && typeof helper === "function"
4968
5094
  ? helper
@@ -5021,6 +5147,7 @@ function getSystemCallTable() {
5021
5147
  "$.Toybox.Lang.Dictionary.toString": nop,
5022
5148
  "$.Toybox.Lang.Dictionary.values": dictionaryValues,
5023
5149
  "$.Toybox.Lang.Method.invoke": methodInvoke,
5150
+ "$.Toybox.Lang.Object.method": method,
5024
5151
  "$.Toybox.Math.acos": mathHelper,
5025
5152
  "$.Toybox.Math.asin": mathHelper,
5026
5153
  "$.Toybox.Math.atan": mathHelper,
@@ -5028,15 +5155,15 @@ function getSystemCallTable() {
5028
5155
  "$.Toybox.Math.ceil": rounder,
5029
5156
  "$.Toybox.Math.cos": mathHelper,
5030
5157
  "$.Toybox.Math.floor": rounder,
5031
- "$.Toybox.Math.ln": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, "log"),
5032
- "$.Toybox.Math.log": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (x, base) => Math.log(x) / Math.log(base)),
5158
+ "$.Toybox.Math.ln": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, "log"),
5159
+ "$.Toybox.Math.log": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (x, base) => Math.log(x) / Math.log(base)),
5033
5160
  "$.Toybox.Math.pow": mathHelper,
5034
5161
  "$.Toybox.Math.round": rounder,
5035
5162
  "$.Toybox.Math.sin": mathHelper,
5036
5163
  "$.Toybox.Math.sqrt": mathHelper,
5037
5164
  "$.Toybox.Math.tan": mathHelper,
5038
- "$.Toybox.Math.toDegrees": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (arg) => (arg * 180) / Math.PI),
5039
- "$.Toybox.Math.toRadians": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (arg) => (arg * Math.PI) / 180),
5165
+ "$.Toybox.Math.toDegrees": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (arg) => (arg * 180) / Math.PI),
5166
+ "$.Toybox.Math.toRadians": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (arg) => (arg * Math.PI) / 180),
5040
5167
  "$.Toybox.Math.mean": nop,
5041
5168
  "$.Toybox.Math.mode": nop,
5042
5169
  "$.Toybox.Math.stdev": nop,
@@ -5288,9 +5415,9 @@ function getLhsConstraint(istate, node) {
5288
5415
  }
5289
5416
  const object = istate.typeMap.get(node.object);
5290
5417
  if (object && !node.computed) {
5291
- const objDecls = findObjectDeclsByProperty(istate.state, object, node);
5292
- if (objDecls) {
5293
- lookupDefs = lookupNext(istate.state, [{ parent: null, results: objDecls }], "decls", node.property);
5418
+ const [, trueDecls] = findObjectDeclsByProperty(istate.state, object, node);
5419
+ if (trueDecls) {
5420
+ lookupDefs = lookupNext(istate.state, [{ parent: null, results: trueDecls }], "decls", node.property);
5294
5421
  }
5295
5422
  }
5296
5423
  }
@@ -6643,6 +6770,8 @@ function printBlockTrailer(block) {
6643
6770
  /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_util__WEBPACK_IMPORTED_MODULE_4__);
6644
6771
  /* harmony import */ var _interp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7161);
6645
6772
  /* harmony import */ var _union_type__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(757);
6773
+ /* harmony import */ var _intersection_type__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(6973);
6774
+
6646
6775
 
6647
6776
 
6648
6777
 
@@ -6866,24 +6995,47 @@ function typeFromTypeStateNode(state, sn, classVsObj) {
6866
6995
  sn.resolvedType = result;
6867
6996
  return result;
6868
6997
  }
6869
- case "VariableDeclarator":
6998
+ case "VariableDeclarator": {
6999
+ if (sn.resolvedType)
7000
+ return sn.resolvedType;
7001
+ let declared = null;
7002
+ if (sn.node.id.type === "BinaryExpression") {
7003
+ declared = typeFromTypespec(state, sn.node.id.right, sn.stack);
7004
+ }
6870
7005
  if (sn.node.kind === "const" && sn.node.init) {
6871
- let node = sn.node.init;
6872
- if (node.type === "Literal") {
6873
- return typeFromLiteral(node);
7006
+ if (hasProperty(sn, "resolvedType")) {
7007
+ // The constant is defined recursively
7008
+ return declared ?? { type: 524287 /* TypeTag.Any */ };
7009
+ }
7010
+ // set the marker in case the constant appears in its
7011
+ // own initializer.
7012
+ sn.resolvedType = undefined;
7013
+ const stack = state.stack;
7014
+ let resolved;
7015
+ try {
7016
+ state.stack = sn.stack;
7017
+ resolved = evaluateExpr(state, sn.node.init).value;
7018
+ }
7019
+ finally {
7020
+ state.stack = stack;
6874
7021
  }
6875
- while (node.type === "BinaryExpression" && node.operator === "as") {
6876
- node = node.left;
7022
+ if (resolved.type === 0 /* TypeTag.Never */) {
7023
+ resolved = declared ?? { type: 524287 /* TypeTag.Any */ };
6877
7024
  }
6878
- if (node.type === "Literal" ||
6879
- (node.type === "UnaryExpression" && node.operator === ":")) {
6880
- return evaluateExpr(state, sn.node.init).value;
7025
+ else if (declared) {
7026
+ resolved = intersection(resolved, declared);
7027
+ if (resolved.type === 0 /* TypeTag.Never */) {
7028
+ resolved = declared;
7029
+ }
6881
7030
  }
7031
+ sn.resolvedType = resolved;
7032
+ return resolved;
6882
7033
  }
6883
7034
  if (sn.node.id.type === "BinaryExpression") {
6884
7035
  return typeFromTypespec(state, sn.node.id.right, sn.stack);
6885
7036
  }
6886
7037
  return { type: 524287 /* TypeTag.Any */ };
7038
+ }
6887
7039
  }
6888
7040
  throw new Error(`Internal error: Unexpected StateNodeDecl.type: ${sn.type}`);
6889
7041
  }
@@ -7217,8 +7369,8 @@ function display(type) {
7217
7369
  case 1024 /* TypeTag.Dictionary */:
7218
7370
  return `${display(tv.value.key)}, ${display(tv.value.value)}`;
7219
7371
  case 2048 /* TypeTag.Method */:
7220
- return `(${tv.value.args
7221
- .map((arg) => display(arg))
7372
+ return `Method(${tv.value.args
7373
+ .map((arg, i) => `a${i + 1} as ${display(arg)}`)
7222
7374
  .join(", ")}) as ${display(tv.value.result)}`;
7223
7375
  case 4096 /* TypeTag.Module */:
7224
7376
  case 8192 /* TypeTag.Function */:
@@ -7278,6 +7430,7 @@ function display(type) {
7278
7430
  65536 /* TypeTag.Enum */ |
7279
7431
  262144 /* TypeTag.Typedef */ |
7280
7432
  131072 /* TypeTag.Symbol */ |
7433
+ 2048 /* TypeTag.Method */ |
7281
7434
  256 /* TypeTag.String */)) {
7282
7435
  parts.push(valueStr);
7283
7436
  }
@@ -7305,7 +7458,7 @@ function forEachUnionComponent(v, bits, fn) {
7305
7458
  bits &= ~SingleTonTypeTagsConst;
7306
7459
  if (!bits)
7307
7460
  return;
7308
- if (v.type & UnionDataTypeTagsConst) {
7461
+ if ((v.type | bits) & UnionDataTypeTagsConst) {
7309
7462
  // Don't iterate the value type bits if any union bit is set
7310
7463
  bits &= ~ValueTypeTagsConst;
7311
7464
  }
@@ -8602,7 +8755,7 @@ function visitReferences(state, ast, name, defn, callback, includeDefs = false,
8602
8755
  const objectType = typeMap.get(node.object);
8603
8756
  if (!objectType)
8604
8757
  return results;
8605
- const decls = (0,type_flow/* findObjectDeclsByProperty */.nK)(state, objectType, node);
8758
+ const [, decls] = (0,type_flow/* findObjectDeclsByProperty */.nK)(state, objectType, node);
8606
8759
  if (decls) {
8607
8760
  const next = (0,external_api_cjs_.lookupNext)(state, [{ parent: null, results: decls }], "decls", node.property);
8608
8761
  if (next) {
@@ -9257,6 +9257,7 @@ function widenType(t) {
9257
9257
 
9258
9258
 
9259
9259
 
9260
+
9260
9261
  function typeTagName(tag) {
9261
9262
  switch (tag) {
9262
9263
  case 0 /* TypeTag.Never */:
@@ -9473,24 +9474,47 @@ function typeFromTypeStateNode(state, sn, classVsObj) {
9473
9474
  sn.resolvedType = result;
9474
9475
  return result;
9475
9476
  }
9476
- case "VariableDeclarator":
9477
+ case "VariableDeclarator": {
9478
+ if (sn.resolvedType)
9479
+ return sn.resolvedType;
9480
+ let declared = null;
9481
+ if (sn.node.id.type === "BinaryExpression") {
9482
+ declared = typeFromTypespec(state, sn.node.id.right, sn.stack);
9483
+ }
9477
9484
  if (sn.node.kind === "const" && sn.node.init) {
9478
- let node = sn.node.init;
9479
- if (node.type === "Literal") {
9480
- return typeFromLiteral(node);
9485
+ if (hasProperty(sn, "resolvedType")) {
9486
+ // The constant is defined recursively
9487
+ return declared ?? { type: 524287 /* TypeTag.Any */ };
9488
+ }
9489
+ // set the marker in case the constant appears in its
9490
+ // own initializer.
9491
+ sn.resolvedType = undefined;
9492
+ const stack = state.stack;
9493
+ let resolved;
9494
+ try {
9495
+ state.stack = sn.stack;
9496
+ resolved = evaluateExpr(state, sn.node.init).value;
9481
9497
  }
9482
- while (node.type === "BinaryExpression" && node.operator === "as") {
9483
- node = node.left;
9498
+ finally {
9499
+ state.stack = stack;
9484
9500
  }
9485
- if (node.type === "Literal" ||
9486
- (node.type === "UnaryExpression" && node.operator === ":")) {
9487
- return evaluateExpr(state, sn.node.init).value;
9501
+ if (resolved.type === 0 /* TypeTag.Never */) {
9502
+ resolved = declared ?? { type: 524287 /* TypeTag.Any */ };
9503
+ }
9504
+ else if (declared) {
9505
+ resolved = intersection(resolved, declared);
9506
+ if (resolved.type === 0 /* TypeTag.Never */) {
9507
+ resolved = declared;
9508
+ }
9488
9509
  }
9510
+ sn.resolvedType = resolved;
9511
+ return resolved;
9489
9512
  }
9490
9513
  if (sn.node.id.type === "BinaryExpression") {
9491
9514
  return typeFromTypespec(state, sn.node.id.right, sn.stack);
9492
9515
  }
9493
9516
  return { type: 524287 /* TypeTag.Any */ };
9517
+ }
9494
9518
  }
9495
9519
  throw new Error(`Internal error: Unexpected StateNodeDecl.type: ${sn.type}`);
9496
9520
  }
@@ -9824,8 +9848,8 @@ function display(type) {
9824
9848
  case 1024 /* TypeTag.Dictionary */:
9825
9849
  return `${display(tv.value.key)}, ${display(tv.value.value)}`;
9826
9850
  case 2048 /* TypeTag.Method */:
9827
- return `(${tv.value.args
9828
- .map((arg) => display(arg))
9851
+ return `Method(${tv.value.args
9852
+ .map((arg, i) => `a${i + 1} as ${display(arg)}`)
9829
9853
  .join(", ")}) as ${display(tv.value.result)}`;
9830
9854
  case 4096 /* TypeTag.Module */:
9831
9855
  case 8192 /* TypeTag.Function */:
@@ -9885,6 +9909,7 @@ function display(type) {
9885
9909
  65536 /* TypeTag.Enum */ |
9886
9910
  262144 /* TypeTag.Typedef */ |
9887
9911
  131072 /* TypeTag.Symbol */ |
9912
+ 2048 /* TypeTag.Method */ |
9888
9913
  256 /* TypeTag.String */)) {
9889
9914
  parts.push(valueStr);
9890
9915
  }
@@ -9912,7 +9937,7 @@ function forEachUnionComponent(v, bits, fn) {
9912
9937
  bits &= ~SingleTonTypeTagsConst;
9913
9938
  if (!bits)
9914
9939
  return;
9915
- if (v.type & UnionDataTypeTagsConst) {
9940
+ if ((v.type | bits) & UnionDataTypeTagsConst) {
9916
9941
  // Don't iterate the value type bits if any union bit is set
9917
9942
  bits &= ~ValueTypeTagsConst;
9918
9943
  }
@@ -10580,6 +10605,8 @@ function evaluateLogicalTypes(op, left, right) {
10580
10605
 
10581
10606
 
10582
10607
 
10608
+
10609
+
10583
10610
  function evaluateCall(istate, node, callee, args) {
10584
10611
  while (!hasValue(callee) || callee.type !== 8192 /* TypeTag.Function */) {
10585
10612
  const name = node.callee.type === "Identifier"
@@ -10602,6 +10629,20 @@ function evaluateCall(istate, node, callee, args) {
10602
10629
  }
10603
10630
  return checkCallArgs(istate, node, callee.value, args);
10604
10631
  }
10632
+ function calleeObjectType(istate, callee) {
10633
+ if (callee.type === "MemberExpression") {
10634
+ return (istate.typeMap?.get(callee.object) || {
10635
+ type: 524287 /* TypeTag.Any */,
10636
+ });
10637
+ }
10638
+ if (callee.type === "Identifier" && istate.func) {
10639
+ const func = istate.func;
10640
+ const [self] = func.stack.slice(-1);
10641
+ return typeFromTypeStateNode(istate.state, self, (func.attributes & StateNodeAttributes.STATIC) !== 0 ||
10642
+ self.type !== "ClassDeclaration");
10643
+ }
10644
+ return null;
10645
+ }
10605
10646
  function checkCallArgs(istate, node, callees, args) {
10606
10647
  const allDiags = [];
10607
10648
  const resultType = (0,external_util_cjs_namespaceObject.reduce)(callees, (result, cur) => {
@@ -10611,13 +10652,11 @@ function checkCallArgs(istate, node, callees, args) {
10611
10652
  let returnType = null;
10612
10653
  let effects = true;
10613
10654
  let argEffects = true;
10614
- if (node.callee.type === "MemberExpression") {
10615
- const object = istate.typeMap?.get(node.callee.object) || {
10616
- type: 524287 /* TypeTag.Any */,
10617
- };
10655
+ const object = calleeObjectType(istate, node.callee);
10656
+ if (object) {
10618
10657
  const info = sysCallInfo(cur);
10619
10658
  if (info) {
10620
- const result = info(cur, object, () => args);
10659
+ const result = info(istate.state, cur, object, () => args);
10621
10660
  if (result.argTypes)
10622
10661
  argTypes = result.argTypes;
10623
10662
  if (result.returnType)
@@ -10756,7 +10795,7 @@ function sysCallInfo(func) {
10756
10795
  function getSystemCallTable() {
10757
10796
  if (systemCallInfo)
10758
10797
  return systemCallInfo;
10759
- const arrayAdd = (callee, calleeObj, getArgs) => {
10798
+ const arrayAdd = (state, callee, calleeObj, getArgs) => {
10760
10799
  const ret = {};
10761
10800
  if (calleeObj.type & 512 /* TypeTag.Array */) {
10762
10801
  const adata = getUnionComponent(calleeObj, 512 /* TypeTag.Array */);
@@ -10788,7 +10827,7 @@ function getSystemCallTable() {
10788
10827
  }
10789
10828
  return ret;
10790
10829
  };
10791
- const arrayRet = (callee, calleeObj, _getArgs) => {
10830
+ const arrayRet = (state, callee, calleeObj, _getArgs) => {
10792
10831
  const ret = { effectFree: true };
10793
10832
  if (calleeObj.type & 512 /* TypeTag.Array */) {
10794
10833
  const adata = getUnionComponent(calleeObj, 512 /* TypeTag.Array */);
@@ -10798,7 +10837,7 @@ function getSystemCallTable() {
10798
10837
  }
10799
10838
  return ret;
10800
10839
  };
10801
- const dictionaryGet = (callee, calleeObj, getArgs) => {
10840
+ const dictionaryGet = (state, callee, calleeObj, getArgs) => {
10802
10841
  const ret = { effectFree: true };
10803
10842
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
10804
10843
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -10812,7 +10851,7 @@ function getSystemCallTable() {
10812
10851
  }
10813
10852
  return ret;
10814
10853
  };
10815
- const dictionaryValues = (callee, calleeObj) => {
10854
+ const dictionaryValues = (state, callee, calleeObj) => {
10816
10855
  const ret = { effectFree: true };
10817
10856
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
10818
10857
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -10822,7 +10861,7 @@ function getSystemCallTable() {
10822
10861
  }
10823
10862
  return ret;
10824
10863
  };
10825
- const dictionaryKeys = (callee, calleeObj) => {
10864
+ const dictionaryKeys = (state, callee, calleeObj) => {
10826
10865
  const ret = { effectFree: true };
10827
10866
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
10828
10867
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -10832,7 +10871,7 @@ function getSystemCallTable() {
10832
10871
  }
10833
10872
  return ret;
10834
10873
  };
10835
- const dictionaryPut = (callee, calleeObj, getArgs) => {
10874
+ const dictionaryPut = (state, callee, calleeObj, getArgs) => {
10836
10875
  const ret = {};
10837
10876
  if (calleeObj.type & 1024 /* TypeTag.Dictionary */) {
10838
10877
  const ddata = getUnionComponent(calleeObj, 1024 /* TypeTag.Dictionary */);
@@ -10863,7 +10902,7 @@ function getSystemCallTable() {
10863
10902
  }
10864
10903
  return ret;
10865
10904
  };
10866
- const methodInvoke = (callee, calleeObj, getArgs) => {
10905
+ const methodInvoke = (state, callee, calleeObj, getArgs) => {
10867
10906
  const ret = { argEffects: true };
10868
10907
  if (calleeObj.type & 2048 /* TypeTag.Method */) {
10869
10908
  const data = getUnionComponent(calleeObj, 2048 /* TypeTag.Method */);
@@ -10876,9 +10915,49 @@ function getSystemCallTable() {
10876
10915
  ret.argTypes = getArgs();
10877
10916
  return ret;
10878
10917
  };
10918
+ const method = (state, callee, calleeObj, getArgs) => {
10919
+ const ret = {};
10920
+ const args = getArgs();
10921
+ if (args.length === 1 &&
10922
+ hasValue(args[0]) &&
10923
+ args[0].type === 131072 /* TypeTag.Symbol */) {
10924
+ const symbol = {
10925
+ type: "Identifier",
10926
+ name: args[0].value,
10927
+ };
10928
+ const next = {
10929
+ type: "MemberExpression",
10930
+ object: symbol,
10931
+ property: symbol,
10932
+ computed: false,
10933
+ };
10934
+ const [, trueDecls] = findObjectDeclsByProperty(state, calleeObj, next);
10935
+ if (!trueDecls)
10936
+ return ret;
10937
+ const callees = trueDecls
10938
+ .flatMap((decl) => decl.decls?.[symbol.name])
10939
+ .filter((decl) => decl?.type === "FunctionDeclaration");
10940
+ if (!callees.length)
10941
+ return ret;
10942
+ ret.returnType = callees.reduce((type, callee) => {
10943
+ const result = callee.node.returnType
10944
+ ? typeFromTypespec(state, callee.node.returnType.argument, callee.stack)
10945
+ : { type: 524287 /* TypeTag.Any */ };
10946
+ const args = callee.node.params.map((param) => param.type === "BinaryExpression"
10947
+ ? typeFromTypespec(state, param.right, callee.stack)
10948
+ : { type: 524287 /* TypeTag.Any */ });
10949
+ unionInto(type, {
10950
+ type: 2048 /* TypeTag.Method */,
10951
+ value: { result, args },
10952
+ });
10953
+ return type;
10954
+ }, { type: 0 /* TypeTag.Never */ });
10955
+ }
10956
+ return ret;
10957
+ };
10879
10958
  const nop = () => ({ effectFree: true });
10880
10959
  const mod = () => ({});
10881
- const rounder = (callee, calleeObj, getArgs) => {
10960
+ const rounder = (state, callee, calleeObj, getArgs) => {
10882
10961
  const results = {};
10883
10962
  const fn = Math[callee.name];
10884
10963
  results.effectFree = true;
@@ -10896,7 +10975,7 @@ function getSystemCallTable() {
10896
10975
  : { type: 120 /* TypeTag.Numeric */ };
10897
10976
  return results;
10898
10977
  };
10899
- const mathHelper = (callee, calleeObj, getArgs, helper) => {
10978
+ const mathHelper = (state, callee, calleeObj, getArgs, helper) => {
10900
10979
  const results = {};
10901
10980
  const fn = helper && typeof helper === "function"
10902
10981
  ? helper
@@ -10955,6 +11034,7 @@ function getSystemCallTable() {
10955
11034
  "$.Toybox.Lang.Dictionary.toString": nop,
10956
11035
  "$.Toybox.Lang.Dictionary.values": dictionaryValues,
10957
11036
  "$.Toybox.Lang.Method.invoke": methodInvoke,
11037
+ "$.Toybox.Lang.Object.method": method,
10958
11038
  "$.Toybox.Math.acos": mathHelper,
10959
11039
  "$.Toybox.Math.asin": mathHelper,
10960
11040
  "$.Toybox.Math.atan": mathHelper,
@@ -10962,15 +11042,15 @@ function getSystemCallTable() {
10962
11042
  "$.Toybox.Math.ceil": rounder,
10963
11043
  "$.Toybox.Math.cos": mathHelper,
10964
11044
  "$.Toybox.Math.floor": rounder,
10965
- "$.Toybox.Math.ln": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, "log"),
10966
- "$.Toybox.Math.log": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (x, base) => Math.log(x) / Math.log(base)),
11045
+ "$.Toybox.Math.ln": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, "log"),
11046
+ "$.Toybox.Math.log": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (x, base) => Math.log(x) / Math.log(base)),
10967
11047
  "$.Toybox.Math.pow": mathHelper,
10968
11048
  "$.Toybox.Math.round": rounder,
10969
11049
  "$.Toybox.Math.sin": mathHelper,
10970
11050
  "$.Toybox.Math.sqrt": mathHelper,
10971
11051
  "$.Toybox.Math.tan": mathHelper,
10972
- "$.Toybox.Math.toDegrees": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (arg) => (arg * 180) / Math.PI),
10973
- "$.Toybox.Math.toRadians": (callee, calleeObj, getArgs) => mathHelper(callee, calleeObj, getArgs, (arg) => (arg * Math.PI) / 180),
11052
+ "$.Toybox.Math.toDegrees": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (arg) => (arg * 180) / Math.PI),
11053
+ "$.Toybox.Math.toRadians": (state, callee, calleeObj, getArgs) => mathHelper(state, callee, calleeObj, getArgs, (arg) => (arg * Math.PI) / 180),
10974
11054
  "$.Toybox.Math.mean": nop,
10975
11055
  "$.Toybox.Math.mode": nop,
10976
11056
  "$.Toybox.Math.stdev": nop,
@@ -11203,9 +11283,9 @@ function getLhsConstraint(istate, node) {
11203
11283
  }
11204
11284
  const object = istate.typeMap.get(node.object);
11205
11285
  if (object && !node.computed) {
11206
- const objDecls = findObjectDeclsByProperty(istate.state, object, node);
11207
- if (objDecls) {
11208
- lookupDefs = (0,external_api_cjs_namespaceObject.lookupNext)(istate.state, [{ parent: null, results: objDecls }], "decls", node.property);
11286
+ const [, trueDecls] = findObjectDeclsByProperty(istate.state, object, node);
11287
+ if (trueDecls) {
11288
+ lookupDefs = (0,external_api_cjs_namespaceObject.lookupNext)(istate.state, [{ parent: null, results: trueDecls }], "decls", node.property);
11209
11289
  }
11210
11290
  }
11211
11291
  }
@@ -12009,45 +12089,115 @@ function printBlockState(block, state, indent = "") {
12009
12089
  console.log(`${indent} - ${typeStateEntry(value, key)}${value.equivSet ? " " + tsEquivs(state, key) : ""}`);
12010
12090
  });
12011
12091
  }
12012
- function filterDecls(decls, possible) {
12092
+ /*
12093
+ * We have an object, and a MemberExpression object.<name>
12094
+ * - decls are the StateNodes associated with the known type
12095
+ * of object.
12096
+ * - possible are all the StateNodes that declare <name>
12097
+ *
12098
+ * We want to find all the elements of possible which are
12099
+ * "compatible" with decls, which tells us the set of things
12100
+ * that object.<name> could correspond to, and also what that
12101
+ * tells us about object.
12102
+ *
12103
+ * The return value is two arrays of StateNode. The first
12104
+ * gives the refined type of object, and the second is the
12105
+ * array of StateNodes that could declare <name>
12106
+ */
12107
+ function filterDecls(decls, possible, name) {
12013
12108
  if (!possible)
12014
- return null;
12015
- return decls.reduce((cur, decl) => {
12109
+ return [null, null];
12110
+ const result = decls.reduce((cur, decl) => {
12016
12111
  const found = possible.reduce((flag, poss) => {
12017
12112
  if (decl === poss ||
12018
- (poss.type === "ClassDeclaration" &&
12019
- (0,external_api_cjs_namespaceObject.getSuperClasses)(poss)?.has(decl)) ||
12020
- (decl.type === "ClassDeclaration" && (0,external_api_cjs_namespaceObject.getSuperClasses)(decl)?.has(poss))) {
12021
- if (!cur)
12022
- cur = [poss];
12023
- else
12024
- cur.push(poss);
12113
+ (poss.type === "ClassDeclaration" && (0,external_api_cjs_namespaceObject.getSuperClasses)(poss)?.has(decl))) {
12114
+ // poss extends decl, so decl must actually be a poss
12115
+ // eg we know obj is an Object, and we call obj.toNumber
12116
+ // so possible includes all the classes that declare toNumber
12117
+ // so we can refine obj's type to the union of those types
12118
+ if (!cur[0]) {
12119
+ cur = [new Set(), new Set()];
12120
+ }
12121
+ cur[0].add(poss);
12122
+ cur[1].add(poss);
12123
+ return true;
12124
+ }
12125
+ else if (decl.type === "ClassDeclaration" &&
12126
+ (0,external_api_cjs_namespaceObject.getSuperClasses)(decl)?.has(poss)) {
12127
+ // decl extends poss, so decl remains unchanged
12128
+ // eg we know obj is Menu2, we call obj.toString
12129
+ // Menu2 doesn't define toString, but Object does
12130
+ // so poss is Object. But we still know that
12131
+ // obj is Menu2
12132
+ if (!cur[0]) {
12133
+ cur = [new Set(), new Set()];
12134
+ }
12135
+ cur[0].add(decl);
12136
+ cur[1].add(poss);
12025
12137
  return true;
12026
12138
  }
12027
12139
  return flag;
12028
12140
  }, false);
12029
12141
  if (!found) {
12030
- possible.some((poss) => {
12031
- if (decl.stack?.some((sn) => sn.decls === poss.decls)) {
12032
- if (!cur)
12033
- cur = [poss];
12034
- else
12035
- cur.push(poss);
12036
- return true;
12037
- }
12038
- return false;
12039
- });
12142
+ // If we didn't find the property in any of the
12143
+ // standard places, the runtime might still find
12144
+ // it by searching up the Module stack (and up
12145
+ // the module stack from any super classes)
12146
+ //
12147
+ // eg
12148
+ //
12149
+ // obj = Application.getApp();
12150
+ // obj.Properties.whatever
12151
+ //
12152
+ // Properties doesn't exist on AppBase, but AppBase
12153
+ // is declared in Application, and Application
12154
+ // does declare Properties. So Application.Properties
12155
+ // is (one of) the declarations we should find; but we
12156
+ // must not refine obj's type to include Application.
12157
+ let d = [decl];
12158
+ do {
12159
+ d.forEach((d) => {
12160
+ const stack = d.stack;
12161
+ possible.forEach((poss) => {
12162
+ for (let i = stack.length; i--;) {
12163
+ const sn = stack[i];
12164
+ if (sn.decls === poss.decls) {
12165
+ if (!cur[0]) {
12166
+ cur = [new Set(), new Set()];
12167
+ }
12168
+ cur[0].add(decl);
12169
+ cur[1].add(poss);
12170
+ break;
12171
+ }
12172
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(sn.decls, name)) {
12173
+ break;
12174
+ }
12175
+ }
12176
+ });
12177
+ });
12178
+ d = d.flatMap((d) => {
12179
+ if (d.type !== "ClassDeclaration" ||
12180
+ !d.superClass ||
12181
+ d.superClass === true) {
12182
+ return [];
12183
+ }
12184
+ return d.superClass;
12185
+ });
12186
+ } while (d.length);
12040
12187
  }
12041
12188
  return cur;
12042
- }, null);
12189
+ }, [null, null]);
12190
+ if (!result[0])
12191
+ return [null, null];
12192
+ return [Array.from(result[0]), Array.from(result[1])];
12043
12193
  }
12044
12194
  function findObjectDeclsByProperty(state, object, next) {
12045
12195
  const decls = getStateNodeDeclsFromType(state, object);
12046
12196
  if (!decls)
12047
- return null;
12197
+ return [null, null];
12048
12198
  const possibleDecls = (0,external_api_cjs_namespaceObject.hasProperty)(state.allDeclarations, next.property.name) &&
12049
12199
  state.allDeclarations[next.property.name];
12050
- return filterDecls(decls, possibleDecls);
12200
+ return filterDecls(decls, possibleDecls, next.property.name);
12051
12201
  }
12052
12202
  function refineObjectTypeByDecls(istate, object, trueDecls) {
12053
12203
  const refinedType = typeFromTypeStateNodes(istate.state, trueDecls);
@@ -12063,13 +12213,13 @@ function findNextObjectType(istate, trueDecls, next) {
12063
12213
  }, { type: 0 /* TypeTag.Never */ });
12064
12214
  }
12065
12215
  function resolveDottedMember(istate, object, next) {
12066
- const decls = findObjectDeclsByProperty(istate.state, object, next);
12067
- if (!decls)
12216
+ const [objDecls, trueDecls] = findObjectDeclsByProperty(istate.state, object, next);
12217
+ if (!objDecls)
12068
12218
  return null;
12069
- const property = findNextObjectType(istate, decls, next);
12219
+ const property = findNextObjectType(istate, trueDecls, next);
12070
12220
  if (!property)
12071
12221
  return null;
12072
- const type = refineObjectTypeByDecls(istate, object, decls);
12222
+ const type = refineObjectTypeByDecls(istate, object, objDecls);
12073
12223
  const mayThrow = !subtypeOf(object, type);
12074
12224
  return { mayThrow, object: type, property };
12075
12225
  }
@@ -12106,11 +12256,11 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
12106
12256
  next = value.obj[me.property.name];
12107
12257
  }
12108
12258
  else {
12109
- const trueDecls = findObjectDeclsByProperty(istate.state, cur, me);
12110
- if (!trueDecls) {
12259
+ const [objDecls, trueDecls] = findObjectDeclsByProperty(istate.state, cur, me);
12260
+ if (!objDecls) {
12111
12261
  return null;
12112
12262
  }
12113
- cur = refineObjectTypeByDecls(istate, cur, trueDecls);
12263
+ cur = refineObjectTypeByDecls(istate, cur, objDecls);
12114
12264
  next = findNextObjectType(istate, trueDecls, me);
12115
12265
  }
12116
12266
  }
@@ -12297,7 +12447,7 @@ function propagateTypes(state, func, graph, optimizeEquivalencies, logThisRun) {
12297
12447
  const info = sysCallInfo(callee);
12298
12448
  if (!info)
12299
12449
  return false;
12300
- const result = info(callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
12450
+ const result = info(istate.state, callee, calleeObj, () => node.arguments.map((arg) => evaluateExpr(state, arg, typeMap).value));
12301
12451
  if (result.calleeObj) {
12302
12452
  setStateEvent(curState, calleeObjDecl, result.calleeObj, false);
12303
12453
  }
@@ -15231,7 +15381,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
15231
15381
  // the oldest optimized file, we don't need to regenerate
15232
15382
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
15233
15383
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
15234
- if (source_time < opt_time && 1674613939459 < opt_time) {
15384
+ if (source_time < opt_time && 1674707539512 < opt_time) {
15235
15385
  return { hasTests, diagnostics: prevDiagnostics };
15236
15386
  }
15237
15387
  }
@@ -15258,7 +15408,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
15258
15408
  return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
15259
15409
  hasTests,
15260
15410
  diagnostics,
15261
- optimizerVersion: "1.1.4",
15411
+ optimizerVersion: "1.1.6",
15262
15412
  ...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
15263
15413
  }))
15264
15414
  .then(() => ({ hasTests, diagnostics }));
@@ -129,6 +129,7 @@ export interface VariableStateNode extends BaseStateNode {
129
129
  fullName: string;
130
130
  stack: ProgramStateStack;
131
131
  used?: true;
132
+ resolvedType?: ExactOrUnion | undefined;
132
133
  }
133
134
  export interface EnumStateNode extends BaseStateNode {
134
135
  type: "EnumDeclaration";
@@ -1,5 +1,5 @@
1
1
  import { mctree } from "@markw65/prettier-plugin-monkeyc";
2
- import { FunctionStateNode } from "src/optimizer-types";
2
+ import { FunctionStateNode, ProgramStateAnalysis } from "../optimizer-types";
3
3
  import { InterpStackElem, InterpState } from "./interp";
4
4
  import { ExactOrUnion } from "./types";
5
5
  export declare function evaluateCall(istate: InterpState, node: mctree.CallExpression, callee: ExactOrUnion, args: ExactOrUnion[]): InterpStackElem;
@@ -11,6 +11,6 @@ declare type SysCallHelperResult = {
11
11
  effectFree?: true;
12
12
  argEffects?: true;
13
13
  };
14
- declare type SysCallHelper = (func: FunctionStateNode, calleeObj: ExactOrUnion, getArgs: () => Array<ExactOrUnion>) => SysCallHelperResult;
14
+ declare type SysCallHelper = (state: ProgramStateAnalysis, func: FunctionStateNode, calleeObj: ExactOrUnion, getArgs: () => Array<ExactOrUnion>) => SysCallHelperResult;
15
15
  export declare function sysCallInfo(func: FunctionStateNode): SysCallHelper | null;
16
16
  export {};
@@ -4,7 +4,7 @@ import { InterpState } from "./type-flow/interp";
4
4
  import { ExactOrUnion } from "./type-flow/types";
5
5
  export declare const missingNullWorkaround = true;
6
6
  export declare function buildTypeInfo(state: ProgramStateAnalysis, func: FunctionStateNode, optimizeEquivalencies: boolean): InterpState | undefined;
7
- export declare function findObjectDeclsByProperty(state: ProgramStateAnalysis, object: ExactOrUnion, next: mctree.DottedMemberExpression): StateNode[] | null;
7
+ export declare function findObjectDeclsByProperty(state: ProgramStateAnalysis, object: ExactOrUnion, next: mctree.DottedMemberExpression): [StateNode[], StateNode[]] | readonly [null, null];
8
8
  export declare function resolveDottedMember(istate: InterpState, object: ExactOrUnion, next: mctree.DottedMemberExpression): {
9
9
  mayThrow: boolean;
10
10
  object: ExactOrUnion;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markw65/monkeyc-optimizer",
3
3
  "type": "module",
4
- "version": "1.1.4",
4
+ "version": "1.1.6",
5
5
  "description": "Source to source optimizer for Garmin Monkey C code",
6
6
  "main": "build/optimizer.cjs",
7
7
  "types": "build/src/optimizer.d.ts",