@markw65/monkeyc-optimizer 1.0.39 → 1.0.40

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 = {collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visitorNode});
1
+ 0 && (module.exports = {checkCompilerVersion,collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,parseSdkVersion,sameLookupResult,traverseAst,variableDeclarationName,visitReferences,visitorNode});
2
2
  /******/ (() => { // webpackBootstrap
3
3
  /******/ var __webpack_modules__ = ({
4
4
 
@@ -258,6 +258,7 @@ __webpack_require__.r(__webpack_exports__);
258
258
 
259
259
  // EXPORTS
260
260
  __webpack_require__.d(__webpack_exports__, {
261
+ "checkCompilerVersion": () => (/* binding */ api_checkCompilerVersion),
261
262
  "collectNamespaces": () => (/* binding */ api_collectNamespaces),
262
263
  "findUsingForNode": () => (/* binding */ findUsingForNode),
263
264
  "formatAst": () => (/* binding */ api_formatAst),
@@ -267,6 +268,7 @@ __webpack_require__.d(__webpack_exports__, {
267
268
  "isLookupCandidate": () => (/* binding */ api_isLookupCandidate),
268
269
  "isStateNode": () => (/* binding */ api_isStateNode),
269
270
  "markInvokeClassMethod": () => (/* binding */ api_markInvokeClassMethod),
271
+ "parseSdkVersion": () => (/* binding */ parseSdkVersion),
270
272
  "sameLookupResult": () => (/* binding */ api_sameLookupResult),
271
273
  "traverseAst": () => (/* reexport */ ast_traverseAst),
272
274
  "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
@@ -651,6 +653,15 @@ function function_info_findCalleesForNew(lookupDefs) {
651
653
  .filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
652
654
  }
653
655
 
656
+ ;// CONCATENATED MODULE: ./src/optimizer-types.ts
657
+ var optimizer_types_StateNodeAttributes;
658
+ (function (StateNodeAttributes) {
659
+ StateNodeAttributes[StateNodeAttributes["PUBLIC"] = 1] = "PUBLIC";
660
+ StateNodeAttributes[StateNodeAttributes["PROTECTED"] = 2] = "PROTECTED";
661
+ StateNodeAttributes[StateNodeAttributes["PRIVATE"] = 4] = "PRIVATE";
662
+ StateNodeAttributes[StateNodeAttributes["STATIC"] = 8] = "STATIC";
663
+ })(optimizer_types_StateNodeAttributes || (optimizer_types_StateNodeAttributes = {}));
664
+
654
665
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
655
666
 
656
667
 
@@ -717,6 +728,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
717
728
 
718
729
 
719
730
 
731
+
720
732
  // Note: Keep in sync with replaceInlinedSubExpression below
721
733
  function inliner_inlinableSubExpression(expr) {
722
734
  while (true) {
@@ -1036,6 +1048,12 @@ function processInlineBody(state, func, call, root, params) {
1036
1048
  state.inlining = true;
1037
1049
  let insertedVariableDecls = null;
1038
1050
  const replacements = new Set();
1051
+ // lookup determines static-ness of the lookup context based on seeing
1052
+ // a static FunctionDeclaration, but the FunctionDeclaration's stack
1053
+ // doesn't include the FunctionDeclaration itself.
1054
+ const stack = func.attributes & StateNodeAttributes.STATIC
1055
+ ? func.stack.concat(func)
1056
+ : func.stack;
1039
1057
  try {
1040
1058
  state.pre = (node) => {
1041
1059
  if (failed)
@@ -1085,7 +1103,7 @@ function processInlineBody(state, func, call, root, params) {
1085
1103
  }
1086
1104
  return null;
1087
1105
  }
1088
- const replacement = fixNodeScope(state, node, func.stack);
1106
+ const replacement = fixNodeScope(state, node, stack);
1089
1107
  if (!replacement) {
1090
1108
  failed = true;
1091
1109
  inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
@@ -1140,7 +1158,7 @@ function processInlineBody(state, func, call, root, params) {
1140
1158
  delete state.inlining;
1141
1159
  }
1142
1160
  }
1143
- function inliner_unused(expression, top) {
1161
+ function inliner_unused(state, expression, top) {
1144
1162
  const estmt = (expression) => withLoc({
1145
1163
  type: "ExpressionStatement",
1146
1164
  expression,
@@ -1154,19 +1172,19 @@ function inliner_unused(expression, top) {
1154
1172
  return [];
1155
1173
  case "BinaryExpression":
1156
1174
  if (expression.operator === "as") {
1157
- return inliner_unused(expression.left);
1175
+ return inliner_unused(state, expression.left);
1158
1176
  }
1159
- return inliner_unused(expression.left).concat(inliner_unused(expression.right));
1177
+ return inliner_unused(state, expression.left).concat(inliner_unused(state, expression.right));
1160
1178
  case "LogicalExpression": {
1161
- const right = inliner_unused(expression.right);
1179
+ const right = inliner_unused(state, expression.right);
1162
1180
  if (!right.length)
1163
- return inliner_unused(expression.left);
1181
+ return inliner_unused(state, expression.left);
1164
1182
  const consequent = withLoc({
1165
1183
  type: "BlockStatement",
1166
1184
  body: [estmt(expression.right)],
1167
1185
  }, expression.right);
1168
1186
  let alternate;
1169
- if (expression.operator == "||") {
1187
+ if (expression.operator == "||" || expression.operator == "or") {
1170
1188
  alternate = { ...consequent };
1171
1189
  consequent.body = [];
1172
1190
  }
@@ -1180,10 +1198,10 @@ function inliner_unused(expression, top) {
1180
1198
  ];
1181
1199
  }
1182
1200
  case "ConditionalExpression": {
1183
- const consequentExprs = inliner_unused(expression.consequent);
1184
- const alternateExprs = inliner_unused(expression.alternate);
1201
+ const consequentExprs = inliner_unused(state, expression.consequent);
1202
+ const alternateExprs = inliner_unused(state, expression.alternate);
1185
1203
  if (!consequentExprs.length && !alternateExprs.length) {
1186
- return inliner_unused(expression.test);
1204
+ return inliner_unused(state, expression.test);
1187
1205
  }
1188
1206
  return [
1189
1207
  withLoc({
@@ -1201,20 +1219,24 @@ function inliner_unused(expression, top) {
1201
1219
  ];
1202
1220
  }
1203
1221
  case "UnaryExpression":
1204
- return inliner_unused(expression.argument);
1222
+ return inliner_unused(state, expression.argument);
1205
1223
  case "MemberExpression":
1206
1224
  if (expression.computed) {
1207
- return inliner_unused(expression.object).concat(inliner_unused(expression.property));
1225
+ return inliner_unused(state, expression.object).concat(inliner_unused(state, expression.property));
1208
1226
  }
1209
- if (expression.object.type === "NewExpression") {
1227
+ if ((state.sdkVersion || 0) < 4001007 &&
1228
+ expression.object.type === "NewExpression") {
1229
+ // prior to 4.1.7 top level new expressions were discarded,
1230
+ // but (new X()).a was not. After 4.1.7, top level new is
1231
+ // executed, but top level (new X()).a is an error.
1210
1232
  break;
1211
1233
  }
1212
- return inliner_unused(expression.object);
1234
+ return inliner_unused(state, expression.object);
1213
1235
  case "ArrayExpression":
1214
- return expression.elements.map((e) => inliner_unused(e)).flat(1);
1236
+ return expression.elements.map((e) => inliner_unused(state, e)).flat(1);
1215
1237
  case "ObjectExpression":
1216
1238
  return expression.properties
1217
- .map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
1239
+ .map((p) => inliner_unused(state, p.key).concat(inliner_unused(state, p.value)))
1218
1240
  .flat(1);
1219
1241
  }
1220
1242
  return top ? null : [estmt(expression)];
@@ -1374,7 +1396,7 @@ function inlineWithArgs(state, func, call, context) {
1374
1396
  ];
1375
1397
  }
1376
1398
  else {
1377
- const side_exprs = inliner_unused(last.argument);
1399
+ const side_exprs = inliner_unused(state, last.argument);
1378
1400
  block.body.splice(block.body.length - 1, 1, ...side_exprs);
1379
1401
  }
1380
1402
  }
@@ -1509,7 +1531,7 @@ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1509
1531
  return;
1510
1532
  diagnostics = diagnostics
1511
1533
  ?.slice()
1512
- .sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
1534
+ .sort((d1, d2) => d1.loc.start.offset - d2.loc.start.offset);
1513
1535
  let diagIndex = 0;
1514
1536
  let index = -1;
1515
1537
  let comment;
@@ -1522,6 +1544,12 @@ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1522
1544
  continue;
1523
1545
  const kind = match[1];
1524
1546
  let str = match[2];
1547
+ const verCheck = checkCompilerVersion(str.replace(/\s.*/, ""), state.sdkVersion || 0);
1548
+ if (verCheck === false)
1549
+ continue;
1550
+ if (verCheck === true) {
1551
+ str = str.replace(/^\S+\s+/, "");
1552
+ }
1525
1553
  matchers = [];
1526
1554
  while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
1527
1555
  matchers.push({ kind, quote: match[1], needle: match[2] });
@@ -3076,7 +3104,7 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3076
3104
  if (node.expression.type === "AssignmentExpression") {
3077
3105
  if (node.expression.left.type === "Identifier" &&
3078
3106
  hasProperty(toRemove, node.expression.left.name)) {
3079
- return unused(node.expression.right);
3107
+ return unused(state, node.expression.right);
3080
3108
  }
3081
3109
  }
3082
3110
  else if (node.expression.type === "UpdateExpression" &&
@@ -3091,7 +3119,7 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3091
3119
  if (expr.type === "AssignmentExpression") {
3092
3120
  if (expr.left.type === "Identifier" &&
3093
3121
  hasProperty(toRemove, expr.left.name)) {
3094
- const rep = unused(expr.right);
3122
+ const rep = unused(state, expr.right);
3095
3123
  if (!rep.length) {
3096
3124
  node.expressions.splice(i, 1);
3097
3125
  }
@@ -3121,13 +3149,16 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3121
3149
  const vdecl = decl.declarations[i];
3122
3150
  const name = variableDeclarationName(vdecl.id);
3123
3151
  if (hasProperty(toRemove, name)) {
3124
- const rep = vdecl.init ? unused(vdecl.init) : [];
3152
+ const rep = vdecl.init ? unused(state, vdecl.init) : [];
3125
3153
  if (rep.length) {
3126
- if (rep.find((s) => s.type === "ExpressionStatement" &&
3127
- (s.expression.type === "NewExpression" ||
3128
- (s.expression.type === "MemberExpression" &&
3129
- !s.expression.computed &&
3130
- s.expression.object.type === "NewExpression")))) {
3154
+ if ((state.sdkVersion || 0) < 4001007 &&
3155
+ rep.find((s) => s.type === "ExpressionStatement" &&
3156
+ (s.expression.type === "NewExpression" ||
3157
+ (s.expression.type === "MemberExpression" &&
3158
+ !s.expression.computed &&
3159
+ s.expression.object.type === "NewExpression")))) {
3160
+ // prior to 4.1.7 vanilla new expressions were discarded,
3161
+ // so don't create top level new expressions.
3131
3162
  continue;
3132
3163
  }
3133
3164
  if (parent.node.type === "ForStatement") {
@@ -3191,6 +3222,7 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3191
3222
 
3192
3223
 
3193
3224
 
3225
+
3194
3226
  function collectClassInfo(state) {
3195
3227
  const toybox = state.stack[0].decls["Toybox"][0];
3196
3228
  const lang = toybox.decls["Lang"][0];
@@ -3266,7 +3298,8 @@ function collectClassInfo(state) {
3266
3298
  if (elm.hasInvoke && elm.decls) {
3267
3299
  Object.values(elm.decls).forEach((funcs) => {
3268
3300
  funcs.forEach((f) => {
3269
- if (f.type === "FunctionDeclaration" && !f.isStatic) {
3301
+ if (f.type === "FunctionDeclaration" &&
3302
+ !(f.attributes & StateNodeAttributes.STATIC)) {
3270
3303
  markInvokeClassMethod(f);
3271
3304
  }
3272
3305
  });
@@ -3346,11 +3379,6 @@ async function analyze(fnMap, resourcesMap, config) {
3346
3379
  const [scope] = state.stack.slice(-1);
3347
3380
  scope.stack = state.stackClone().slice(0, -1);
3348
3381
  if (scope.type == "FunctionDeclaration") {
3349
- scope.isStatic =
3350
- scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
3351
- (scope.node.attrs &&
3352
- scope.node.attrs.access &&
3353
- scope.node.attrs.access.includes("static"));
3354
3382
  if (markApi) {
3355
3383
  node.body = null;
3356
3384
  scope.info = getApiFunctionInfo(scope);
@@ -3397,18 +3425,45 @@ function reportMissingSymbols(state, config) {
3397
3425
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
3398
3426
  ? config?.checkInvalidSymbols || "WARNING"
3399
3427
  : null;
3428
+ const compiler2DiagnosticType = config?.checkCompilerLookupRules !== "OFF"
3429
+ ? config?.checkCompilerLookupRules || "WARNING"
3430
+ : null;
3400
3431
  if (diagnosticType &&
3401
3432
  !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
3402
3433
  const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
3403
3434
  Object.entries(state.fnMap).forEach(([, v]) => {
3404
3435
  visitReferences(state, v.ast, null, false, (node, results, error) => {
3405
- if (!error)
3406
- return undefined;
3407
3436
  if (node.type === "BinaryExpression" && node.operator === "has") {
3408
3437
  // Its not an error to check whether a property exists...
3409
3438
  return undefined;
3410
3439
  }
3411
3440
  const nodeStr = formatAst(node);
3441
+ if (!error) {
3442
+ if (state.sdkVersion === 4001006 &&
3443
+ compiler2DiagnosticType &&
3444
+ node.type === "MemberExpression" &&
3445
+ (node.object.type === "Identifier" ||
3446
+ node.object.type === "MemberExpression") &&
3447
+ results.some((result) => {
3448
+ const parent = result.parent;
3449
+ if (!parent || parent.type !== "ClassDeclaration") {
3450
+ return false;
3451
+ }
3452
+ return result.results.some((sn) => {
3453
+ switch (sn.type) {
3454
+ case "VariableDeclarator":
3455
+ case "FunctionDeclaration":
3456
+ return (sn.attributes &
3457
+ (StateNodeAttributes.PRIVATE |
3458
+ StateNodeAttributes.PROTECTED));
3459
+ }
3460
+ return false;
3461
+ });
3462
+ })) {
3463
+ diagnostic(state, node.loc, `The expression ${nodeStr} will fail at runtime using sdk-4.1.6`, compiler2DiagnosticType);
3464
+ }
3465
+ return undefined;
3466
+ }
3412
3467
  if (state.inType) {
3413
3468
  if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
3414
3469
  return undefined;
@@ -3622,7 +3677,7 @@ function optimizeNode(state, node) {
3622
3677
  left.value === null ||
3623
3678
  ((left_type === "Number" || left_type === "Long") &&
3624
3679
  (left.value === 0 || left.value === 0n));
3625
- if (falsy === (node.operator === "&&")) {
3680
+ if (falsy === (node.operator === "&&" || node.operator === "and")) {
3626
3681
  return left;
3627
3682
  }
3628
3683
  if (left_type !== "Boolean" &&
@@ -3632,10 +3687,12 @@ function optimizeNode(state, node) {
3632
3687
  }
3633
3688
  const [right, right_type] = getNodeValue(node.right);
3634
3689
  if (right && right_type === left_type) {
3635
- if (left_type === "Boolean" || node.operator === "||") {
3690
+ if (left_type === "Boolean" ||
3691
+ node.operator === "||" ||
3692
+ node.operator === "or") {
3636
3693
  return right;
3637
3694
  }
3638
- if (node.operator !== "&&") {
3695
+ if (node.operator !== "&&" && node.operator !== "and") {
3639
3696
  throw new Error(`Unexpected operator "${node.operator}"`);
3640
3697
  }
3641
3698
  return { ...node, type: "BinaryExpression", operator: "&" };
@@ -4125,7 +4182,6 @@ async function optimizeMonkeyC(fnMap, resourcesMap, config) {
4125
4182
  if ((obj.type === "ModuleDeclaration" ||
4126
4183
  obj.type === "Program" ||
4127
4184
  obj.type === "ClassDeclaration") &&
4128
- obj.decls &&
4129
4185
  obj.stack) {
4130
4186
  const exists = hasProperty(obj.decls, node.right.argument.name) ||
4131
4187
  // This is overkill, since we've already looked up
@@ -4226,7 +4282,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, config) {
4226
4282
  }
4227
4283
  }
4228
4284
  else {
4229
- const ret = unused(node.expression, true);
4285
+ const ret = unused(state, node.expression, true);
4230
4286
  if (ret) {
4231
4287
  return ret
4232
4288
  .map((r) => replace(r, r))
@@ -4800,6 +4856,12 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
4800
4856
  }
4801
4857
  return ["returnType"];
4802
4858
  }
4859
+ case "ModuleDeclaration":
4860
+ return ["body"];
4861
+ case "ClassDeclaration":
4862
+ return ["body", "superClass"];
4863
+ case "FunctionDeclaration":
4864
+ return ["params", "returnType", "body"];
4803
4865
  }
4804
4866
  return null;
4805
4867
  };
@@ -4820,6 +4882,7 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
4820
4882
 
4821
4883
 
4822
4884
 
4885
+
4823
4886
  /*
4824
4887
  * This is an unfortunate hack. I want to be able to extract things
4825
4888
  * like the types of all of a Class's variables (in particular the type
@@ -4829,11 +4892,48 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
4829
4892
  * undocumented. The same could be said of compiler.json and simulator.json,
4830
4893
  * but those are at least in a standard format.
4831
4894
  */
4895
+ function parseSdkVersion(version) {
4896
+ if (!version)
4897
+ return 0;
4898
+ const match = version.match(/^(\d+)[._](\d+)[._](\d+)$/);
4899
+ if (!match)
4900
+ return 0;
4901
+ return (parseInt(match[1], 10) * 1000000 +
4902
+ parseInt(match[2], 10) * 1000 +
4903
+ parseInt(match[3], 10));
4904
+ }
4905
+ function api_checkCompilerVersion(version, sdkVer) {
4906
+ const match = version.match(/^(\d+[._]\d+[._]\d+)?([-_])?(\d+[._]\d+[._]\d+)?$/);
4907
+ if (!match ||
4908
+ (match[1] && match[3] && !match[2]) ||
4909
+ (!match[1] && !match[3])) {
4910
+ return undefined;
4911
+ }
4912
+ const v1 = parseSdkVersion(match[1]);
4913
+ const v2 = parseSdkVersion(match[3]);
4914
+ if (v1) {
4915
+ if (v2) {
4916
+ return v1 <= sdkVer && sdkVer <= v2;
4917
+ }
4918
+ if (match[2]) {
4919
+ return v1 <= sdkVer;
4920
+ }
4921
+ return v1 === sdkVer;
4922
+ }
4923
+ return sdkVer <= v2;
4924
+ }
4832
4925
  // Extract all enum values from api.mir
4833
4926
  async function api_getApiMapping(state, resourcesMap) {
4834
4927
  // get the path to the currently active sdk
4835
4928
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
4836
4929
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
4930
+ if (state) {
4931
+ state.sdk = sdk;
4932
+ const match = state.sdk?.match(/-(\d+\.\d+\.\d+)/);
4933
+ if (match) {
4934
+ state.sdkVersion = parseSdkVersion(match[1]);
4935
+ }
4936
+ }
4837
4937
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
4838
4938
  .toString()
4839
4939
  .replace(/\r\n/g, "\n")
@@ -4890,6 +4990,26 @@ function api_isStateNode(node) {
4890
4990
  function api_variableDeclarationName(node) {
4891
4991
  return ("left" in node ? node.left : node).name;
4892
4992
  }
4993
+ function stateNodeAttrs(attrs) {
4994
+ return attrs && attrs.access
4995
+ ? attrs.access.reduce((cur, attr) => {
4996
+ switch (attr) {
4997
+ case "static":
4998
+ return cur | optimizer_types_StateNodeAttributes.STATIC;
4999
+ case "public":
5000
+ return cur | optimizer_types_StateNodeAttributes.PUBLIC;
5001
+ case "protected":
5002
+ return cur | optimizer_types_StateNodeAttributes.PROTECTED;
5003
+ case "hidden":
5004
+ return cur | optimizer_types_StateNodeAttributes.PROTECTED;
5005
+ case "private":
5006
+ return cur | optimizer_types_StateNodeAttributes.PRIVATE;
5007
+ default:
5008
+ return cur;
5009
+ }
5010
+ }, 0)
5011
+ : 0;
5012
+ }
4893
5013
  function lookupToStateNodeDecls(results) {
4894
5014
  return results.reduce((result, current) => current.results.length
4895
5015
  ? result
@@ -4897,7 +5017,7 @@ function lookupToStateNodeDecls(results) {
4897
5017
  : current.results
4898
5018
  : result, null);
4899
5019
  }
4900
- function checkOne(state, ns, decls, node, isStatic) {
5020
+ function checkOne(state, ns, decls, node) {
4901
5021
  // follow the superchain, looking up node in each class
4902
5022
  const superChain = (cls) => {
4903
5023
  if (!cls.superClass || cls.superClass === true) {
@@ -4912,7 +5032,7 @@ function checkOne(state, ns, decls, node, isStatic) {
4912
5032
  }, null);
4913
5033
  };
4914
5034
  const lookupInContext = (ns) => {
4915
- const [, lkup] = lookup(state, decls, node, null, ns.stack);
5035
+ const [, lkup] = lookup(state, decls, node, null, ns.stack, false, true);
4916
5036
  return lkup && lookupToStateNodeDecls(lkup);
4917
5037
  };
4918
5038
  // follow the superchain, looking up node in each class's scope
@@ -4934,10 +5054,7 @@ function checkOne(state, ns, decls, node, isStatic) {
4934
5054
  }
4935
5055
  switch (ns.type) {
4936
5056
  case "ClassDeclaration":
4937
- if (!isStatic) {
4938
- return superChain(ns) || superChainScopes(ns) || false;
4939
- }
4940
- // fall through
5057
+ return superChain(ns) || superChainScopes(ns) || false;
4941
5058
  case "ModuleDeclaration":
4942
5059
  return lookupInContext(ns) || false;
4943
5060
  }
@@ -4985,7 +5102,7 @@ function api_isLookupCandidate(node) {
4985
5102
  * - [false, false] - if the lookup fails, but its not an error because its not the kind of expression we can lookup
4986
5103
  * - [null, null] - if the lookup fails unexpectedly.
4987
5104
  */
4988
- function lookup(state, decls, node, name, maybeStack, nonlocal) {
5105
+ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
4989
5106
  const stack = maybeStack || state.stack;
4990
5107
  switch (node.type) {
4991
5108
  case "MemberExpression": {
@@ -4994,7 +5111,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4994
5111
  break;
4995
5112
  let result;
4996
5113
  if (node.object.type === "ThisExpression") {
4997
- [, result] = lookup(state, decls, node.property, name, stack, true);
5114
+ [, result] = lookup(state, decls, node.property, name, stack, true, true);
4998
5115
  }
4999
5116
  else {
5000
5117
  const [, results] = lookup(state, decls, node.object, name, stack, false);
@@ -5008,7 +5125,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
5008
5125
  if (!api_isStateNode(module)) {
5009
5126
  return null;
5010
5127
  }
5011
- const res = checkOne(state, module, decls, property, false);
5128
+ const res = checkOne(state, module, decls, property);
5012
5129
  return res ? { parent: module, results: res } : null;
5013
5130
  })
5014
5131
  .filter((r) => r != null);
@@ -5051,30 +5168,66 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
5051
5168
  return [name || node.name, [{ parent: null, results: [stack[0]] }]];
5052
5169
  }
5053
5170
  let inStatic = false;
5054
- let checkedImports = false;
5171
+ let checkedImports = ignoreImports;
5172
+ let imports = null;
5055
5173
  for (let i = stack.length; i--;) {
5056
5174
  const si = stack[i];
5057
5175
  switch (si.type) {
5058
5176
  case "ClassDeclaration":
5177
+ if (inStatic && state.config?.enforceStatic != "NO") {
5178
+ inStatic = false;
5179
+ if (ast_hasProperty(si.decls, node.name)) {
5180
+ const r = si.decls[node.name].filter((s) => {
5181
+ switch (s.type) {
5182
+ case "FunctionDeclaration":
5183
+ case "VariableDeclarator":
5184
+ case "ClassDeclaration":
5185
+ // In theory we should include EnumStringMember here too, but
5186
+ // without adding an attributes field to EnumStringMember,
5187
+ // or turning it into a StateNodeDecl, we don't know whether
5188
+ // its static or not. But thats ok, because the optimizer
5189
+ // will replace its use with its value, so the optimized
5190
+ // code *will* work anyway.
5191
+ return s.attributes & optimizer_types_StateNodeAttributes.STATIC;
5192
+ default:
5193
+ return true;
5194
+ }
5195
+ });
5196
+ if (r.length) {
5197
+ return [name || node.name, [{ parent: si, results: r }]];
5198
+ }
5199
+ }
5200
+ continue;
5201
+ }
5202
+ // fall through
5059
5203
  case "ModuleDeclaration":
5060
5204
  case "Program":
5061
5205
  if (!checkedImports) {
5062
5206
  checkedImports = true;
5063
- const results = findUsingForNode(state, stack, i, node, decls === "type_decls");
5207
+ const results = findUsingForNode(state, stack, i, node);
5064
5208
  if (results) {
5065
- return [name || node.name, [{ parent: si, results }]];
5209
+ if (Array.isArray(results)) {
5210
+ if (!imports)
5211
+ imports = results;
5212
+ }
5213
+ else {
5214
+ return [
5215
+ name || node.name,
5216
+ [{ parent: si, results: [results] }],
5217
+ ];
5218
+ }
5066
5219
  }
5067
5220
  }
5068
5221
  break;
5069
5222
  case "FunctionDeclaration":
5070
- inStatic = si.isStatic === true;
5223
+ inStatic = !!(si.attributes & optimizer_types_StateNodeAttributes.STATIC);
5071
5224
  // fall through
5072
5225
  default:
5073
5226
  if (nonlocal)
5074
5227
  continue;
5075
5228
  break;
5076
5229
  }
5077
- const results = checkOne(state, si, decls, node, inStatic);
5230
+ const results = checkOne(state, si, decls, node);
5078
5231
  if (results) {
5079
5232
  return [name || node.name, [{ parent: si, results }]];
5080
5233
  }
@@ -5082,6 +5235,31 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
5082
5235
  break;
5083
5236
  }
5084
5237
  }
5238
+ if (imports) {
5239
+ if (imports.length > 1) {
5240
+ if (state.config?.checkInvalidSymbols !== "OFF") {
5241
+ inliner_diagnostic(state, node.loc, `${api_formatAst(node)} is ambiguous and exists in multiple imported modules [${imports
5242
+ .map(({ name }) => name)
5243
+ .join(", ")}]`, state.config?.checkInvalidSymbols || "WARNING");
5244
+ }
5245
+ else if (decls !== "type_decls" &&
5246
+ state.lookupRules === "COMPILER1") {
5247
+ return [null, null];
5248
+ }
5249
+ return [name || node.name, imports.map((d) => d.decls)];
5250
+ }
5251
+ if (imports.length == 1) {
5252
+ if (decls !== "type_decls") {
5253
+ if (state.config?.checkCompilerLookupRules !== "OFF") {
5254
+ inliner_diagnostic(state, node.loc, `${api_formatAst(node)} will only be found when compiled with compiler2 at -O1 or above`, state.config?.checkCompilerLookupRules || "WARNING");
5255
+ }
5256
+ else if (state.lookupRules === "COMPILER1") {
5257
+ return [null, null];
5258
+ }
5259
+ }
5260
+ return [name || node.name, [imports[0].decls]];
5261
+ }
5262
+ }
5085
5263
  return [null, null];
5086
5264
  }
5087
5265
  }
@@ -5095,9 +5273,27 @@ function api_collectNamespaces(ast, stateIn) {
5095
5273
  state.index = {};
5096
5274
  if (!state.stack) {
5097
5275
  state.stack = [
5098
- { type: "Program", name: "$", fullName: "$", node: undefined },
5276
+ {
5277
+ type: "Program",
5278
+ name: "$",
5279
+ fullName: "$",
5280
+ node: undefined,
5281
+ attributes: 0,
5282
+ },
5099
5283
  ];
5100
5284
  }
5285
+ if (!state.lookupRules) {
5286
+ const rules = state?.config?.compilerLookupRules || "DEFAULT";
5287
+ if (rules !== "COMPILER1" && rules !== "COMPILER2") {
5288
+ const match = state.sdk?.match(/-(\d+\.\d+\.\d+).(compiler2beta)?/i);
5289
+ if (match && (match[2] || parseSdkVersion(match[1]) >= 4001006)) {
5290
+ state.lookupRules = "COMPILER2";
5291
+ }
5292
+ else {
5293
+ state.lookupRules = "COMPILER1";
5294
+ }
5295
+ }
5296
+ }
5101
5297
  state.removeNodeComments = (node, ast) => {
5102
5298
  if (node.start && node.end && ast.comments && ast.comments.length) {
5103
5299
  let low = 0, high = ast.comments.length;
@@ -5192,6 +5388,7 @@ function api_collectNamespaces(ast, stateIn) {
5192
5388
  name: undefined,
5193
5389
  node: node.body,
5194
5390
  decls: { [id.name]: [id] },
5391
+ attributes: 0,
5195
5392
  });
5196
5393
  }
5197
5394
  break;
@@ -5202,6 +5399,7 @@ function api_collectNamespaces(ast, stateIn) {
5202
5399
  fullName: undefined,
5203
5400
  name: undefined,
5204
5401
  node: node,
5402
+ attributes: 0,
5205
5403
  });
5206
5404
  }
5207
5405
  break;
@@ -5229,6 +5427,9 @@ function api_collectNamespaces(ast, stateIn) {
5229
5427
  name,
5230
5428
  fullName,
5231
5429
  node,
5430
+ attributes: node.type === "BlockStatement"
5431
+ ? 0
5432
+ : stateNodeAttrs(node.attrs),
5232
5433
  };
5233
5434
  state.stack.push(elm);
5234
5435
  if (name) {
@@ -5255,9 +5456,6 @@ function api_collectNamespaces(ast, stateIn) {
5255
5456
  parent.decls[name].push(elm);
5256
5457
  if (node.type == "ModuleDeclaration" ||
5257
5458
  node.type == "ClassDeclaration") {
5258
- // Inject the class/module name into itself,
5259
- // so you can say Graphics.Graphics.Graphics.COLOR_RED
5260
- elm.decls = { [name]: [elm] };
5261
5459
  if (!parent.type_decls)
5262
5460
  parent.type_decls = {};
5263
5461
  if (!ast_hasProperty(parent.type_decls, name)) {
@@ -5299,6 +5497,7 @@ function api_collectNamespaces(ast, stateIn) {
5299
5497
  node,
5300
5498
  name,
5301
5499
  fullName: parent.fullName + "." + name,
5500
+ attributes: stateNodeAttrs(node.attrs),
5302
5501
  });
5303
5502
  break;
5304
5503
  }
@@ -5317,12 +5516,13 @@ function api_collectNamespaces(ast, stateIn) {
5317
5516
  return;
5318
5517
  }
5319
5518
  decl.kind = node.kind;
5320
- (0,external_util_cjs_namespaceObject.pushUnique)(decls[name], {
5519
+ decls[name].push({
5321
5520
  type: "VariableDeclarator",
5322
5521
  node: decl,
5323
5522
  name,
5324
5523
  fullName: parent.fullName + "." + name,
5325
5524
  stack,
5525
+ attributes: stateNodeAttrs(node.attrs),
5326
5526
  });
5327
5527
  if (node.kind == "const") {
5328
5528
  if (!ast_hasProperty(state.index, name)) {
@@ -5547,27 +5747,32 @@ function findUsing(state, stack, using) {
5547
5747
  }
5548
5748
  return null;
5549
5749
  }
5550
- function findUsingForNode(state, stack, i, node, isType) {
5750
+ function findUsingForNode(state, stack, i, node) {
5751
+ let imports = null;
5551
5752
  while (i >= 0) {
5552
5753
  const si = stack[i--];
5553
5754
  if (ast_hasProperty(si.usings, node.name)) {
5554
5755
  const using = si.usings[node.name];
5555
- const module = findUsing(state, stack, using);
5556
- return module && [module];
5756
+ return findUsing(state, stack, using);
5557
5757
  }
5558
- if (si.imports && isType) {
5758
+ if (si.imports) {
5559
5759
  for (let j = si.imports.length; j--;) {
5560
5760
  const using = si.imports[j];
5561
5761
  const module = findUsing(state, stack, using);
5562
5762
  if (module) {
5563
5763
  if (ast_hasProperty(module.type_decls, node.name)) {
5564
- return module.type_decls[node.name];
5764
+ if (!imports)
5765
+ imports = [];
5766
+ imports.push({
5767
+ name: `${module.fullName}.${node.name}`,
5768
+ decls: { parent: si, results: module.type_decls[node.name] },
5769
+ });
5565
5770
  }
5566
5771
  }
5567
5772
  }
5568
5773
  }
5569
5774
  }
5570
- return null;
5775
+ return imports;
5571
5776
  }
5572
5777
  const invokeInfo = {};
5573
5778
  const toyboxFnInfo = {};