@markw65/monkeyc-optimizer 1.0.38 → 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,8 +1,8 @@
1
- 0 && (module.exports = {collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,sameLookupResult,traverseAst,variableDeclarationName,visitReferences});
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
 
5
- /***/ 2789:
5
+ /***/ 789:
6
6
  /***/ ((module) => {
7
7
 
8
8
  /**
@@ -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,10 +268,12 @@ __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),
273
- "visitReferences": () => (/* reexport */ visitor_visitReferences)
275
+ "visitReferences": () => (/* reexport */ visitor_visitReferences),
276
+ "visitorNode": () => (/* reexport */ visitorNode)
274
277
  });
275
278
 
276
279
  ;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
@@ -650,6 +653,15 @@ function function_info_findCalleesForNew(lookupDefs) {
650
653
  .filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
651
654
  }
652
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
+
653
665
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
654
666
 
655
667
 
@@ -716,6 +728,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
716
728
 
717
729
 
718
730
 
731
+
719
732
  // Note: Keep in sync with replaceInlinedSubExpression below
720
733
  function inliner_inlinableSubExpression(expr) {
721
734
  while (true) {
@@ -1035,6 +1048,12 @@ function processInlineBody(state, func, call, root, params) {
1035
1048
  state.inlining = true;
1036
1049
  let insertedVariableDecls = null;
1037
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;
1038
1057
  try {
1039
1058
  state.pre = (node) => {
1040
1059
  if (failed)
@@ -1084,7 +1103,7 @@ function processInlineBody(state, func, call, root, params) {
1084
1103
  }
1085
1104
  return null;
1086
1105
  }
1087
- const replacement = fixNodeScope(state, node, func.stack);
1106
+ const replacement = fixNodeScope(state, node, stack);
1088
1107
  if (!replacement) {
1089
1108
  failed = true;
1090
1109
  inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
@@ -1139,7 +1158,7 @@ function processInlineBody(state, func, call, root, params) {
1139
1158
  delete state.inlining;
1140
1159
  }
1141
1160
  }
1142
- function inliner_unused(expression, top) {
1161
+ function inliner_unused(state, expression, top) {
1143
1162
  const estmt = (expression) => withLoc({
1144
1163
  type: "ExpressionStatement",
1145
1164
  expression,
@@ -1153,19 +1172,19 @@ function inliner_unused(expression, top) {
1153
1172
  return [];
1154
1173
  case "BinaryExpression":
1155
1174
  if (expression.operator === "as") {
1156
- return inliner_unused(expression.left);
1175
+ return inliner_unused(state, expression.left);
1157
1176
  }
1158
- return inliner_unused(expression.left).concat(inliner_unused(expression.right));
1177
+ return inliner_unused(state, expression.left).concat(inliner_unused(state, expression.right));
1159
1178
  case "LogicalExpression": {
1160
- const right = inliner_unused(expression.right);
1179
+ const right = inliner_unused(state, expression.right);
1161
1180
  if (!right.length)
1162
- return inliner_unused(expression.left);
1181
+ return inliner_unused(state, expression.left);
1163
1182
  const consequent = withLoc({
1164
1183
  type: "BlockStatement",
1165
1184
  body: [estmt(expression.right)],
1166
1185
  }, expression.right);
1167
1186
  let alternate;
1168
- if (expression.operator == "||") {
1187
+ if (expression.operator == "||" || expression.operator == "or") {
1169
1188
  alternate = { ...consequent };
1170
1189
  consequent.body = [];
1171
1190
  }
@@ -1179,10 +1198,10 @@ function inliner_unused(expression, top) {
1179
1198
  ];
1180
1199
  }
1181
1200
  case "ConditionalExpression": {
1182
- const consequentExprs = inliner_unused(expression.consequent);
1183
- const alternateExprs = inliner_unused(expression.alternate);
1201
+ const consequentExprs = inliner_unused(state, expression.consequent);
1202
+ const alternateExprs = inliner_unused(state, expression.alternate);
1184
1203
  if (!consequentExprs.length && !alternateExprs.length) {
1185
- return inliner_unused(expression.test);
1204
+ return inliner_unused(state, expression.test);
1186
1205
  }
1187
1206
  return [
1188
1207
  withLoc({
@@ -1200,17 +1219,24 @@ function inliner_unused(expression, top) {
1200
1219
  ];
1201
1220
  }
1202
1221
  case "UnaryExpression":
1203
- return inliner_unused(expression.argument);
1222
+ return inliner_unused(state, expression.argument);
1204
1223
  case "MemberExpression":
1205
1224
  if (expression.computed) {
1206
- return inliner_unused(expression.object).concat(inliner_unused(expression.property));
1225
+ return inliner_unused(state, expression.object).concat(inliner_unused(state, expression.property));
1226
+ }
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.
1232
+ break;
1207
1233
  }
1208
- return inliner_unused(expression.object);
1234
+ return inliner_unused(state, expression.object);
1209
1235
  case "ArrayExpression":
1210
- return expression.elements.map((e) => inliner_unused(e)).flat(1);
1236
+ return expression.elements.map((e) => inliner_unused(state, e)).flat(1);
1211
1237
  case "ObjectExpression":
1212
1238
  return expression.properties
1213
- .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)))
1214
1240
  .flat(1);
1215
1241
  }
1216
1242
  return top ? null : [estmt(expression)];
@@ -1370,7 +1396,7 @@ function inlineWithArgs(state, func, call, context) {
1370
1396
  ];
1371
1397
  }
1372
1398
  else {
1373
- const side_exprs = inliner_unused(last.argument);
1399
+ const side_exprs = inliner_unused(state, last.argument);
1374
1400
  block.body.splice(block.body.length - 1, 1, ...side_exprs);
1375
1401
  }
1376
1402
  }
@@ -1505,7 +1531,7 @@ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1505
1531
  return;
1506
1532
  diagnostics = diagnostics
1507
1533
  ?.slice()
1508
- .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);
1509
1535
  let diagIndex = 0;
1510
1536
  let index = -1;
1511
1537
  let comment;
@@ -1518,6 +1544,12 @@ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1518
1544
  continue;
1519
1545
  const kind = match[1];
1520
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
+ }
1521
1553
  matchers = [];
1522
1554
  while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
1523
1555
  matchers.push({ kind, quote: match[1], needle: match[2] });
@@ -2126,7 +2158,7 @@ function getPreOrder(head) {
2126
2158
  }
2127
2159
 
2128
2160
  // EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
2129
- var priorityqueuejs = __webpack_require__(2789);
2161
+ var priorityqueuejs = __webpack_require__(789);
2130
2162
  ;// CONCATENATED MODULE: ./src/pre.ts
2131
2163
 
2132
2164
 
@@ -3072,7 +3104,7 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3072
3104
  if (node.expression.type === "AssignmentExpression") {
3073
3105
  if (node.expression.left.type === "Identifier" &&
3074
3106
  hasProperty(toRemove, node.expression.left.name)) {
3075
- return unused(node.expression.right);
3107
+ return unused(state, node.expression.right);
3076
3108
  }
3077
3109
  }
3078
3110
  else if (node.expression.type === "UpdateExpression" &&
@@ -3087,7 +3119,7 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3087
3119
  if (expr.type === "AssignmentExpression") {
3088
3120
  if (expr.left.type === "Identifier" &&
3089
3121
  hasProperty(toRemove, expr.left.name)) {
3090
- const rep = unused(expr.right);
3122
+ const rep = unused(state, expr.right);
3091
3123
  if (!rep.length) {
3092
3124
  node.expressions.splice(i, 1);
3093
3125
  }
@@ -3117,8 +3149,18 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3117
3149
  const vdecl = decl.declarations[i];
3118
3150
  const name = variableDeclarationName(vdecl.id);
3119
3151
  if (hasProperty(toRemove, name)) {
3120
- const rep = vdecl.init ? unused(vdecl.init) : [];
3152
+ const rep = vdecl.init ? unused(state, vdecl.init) : [];
3121
3153
  if (rep.length) {
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.
3162
+ continue;
3163
+ }
3122
3164
  if (parent.node.type === "ForStatement") {
3123
3165
  // declarations whose inits have side effects
3124
3166
  // can't be deleted from for statements.
@@ -3168,100 +3210,6 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3168
3210
  }
3169
3211
  }
3170
3212
 
3171
- ;// CONCATENATED MODULE: ./src/visitor.ts
3172
-
3173
- function visitor_visitReferences(state, ast, name, defn, callback) {
3174
- const checkResults = ([name, results], node) => {
3175
- if (name && results) {
3176
- if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
3177
- if (callback(node, results, false) === false) {
3178
- return [];
3179
- }
3180
- }
3181
- }
3182
- else if (defn === false) {
3183
- if (callback(node, [], results === null) === false) {
3184
- return [];
3185
- }
3186
- }
3187
- return null;
3188
- };
3189
- state.pre = (node) => {
3190
- switch (node.type) {
3191
- case "AttributeList":
3192
- return [];
3193
- case "UnaryExpression":
3194
- // a bare symbol isn't a reference
3195
- if (node.operator === ":")
3196
- return [];
3197
- break;
3198
- case "BinaryExpression":
3199
- /*
3200
- * `expr has :symbol` can be treated as a reference
3201
- * to expr.symbol.
3202
- */
3203
- if (node.operator === "has") {
3204
- if (node.right.type === "UnaryExpression" &&
3205
- node.right.operator === ":") {
3206
- if (!name || node.right.argument.name === name) {
3207
- return checkResults(state.lookup({
3208
- type: "MemberExpression",
3209
- object: node.left,
3210
- property: node.right.argument,
3211
- computed: false,
3212
- }), node.right.argument);
3213
- }
3214
- }
3215
- }
3216
- break;
3217
- case "CallExpression":
3218
- // A call expression whose callee is an identifier is looked
3219
- // up as a non-local. ie even if there's a same named local,
3220
- // it will be ignored, and the lookup will start as if the
3221
- // call had been written self.foo() rather than foo().
3222
- if (node.callee.type === "Identifier") {
3223
- if (!name || node.callee.name === name) {
3224
- /* ignore return value */
3225
- checkResults(state.lookupNonlocal(node.callee), node.callee);
3226
- }
3227
- return ["arguments"];
3228
- }
3229
- break;
3230
- case "Identifier":
3231
- if (!name || node.name === name) {
3232
- return checkResults(state.lookup(node), node);
3233
- }
3234
- break;
3235
- case "MemberExpression": {
3236
- const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
3237
- if (property) {
3238
- if (!name || property.name === name) {
3239
- return checkResults(state.lookup(node), node) || ["object"];
3240
- }
3241
- return ["object"];
3242
- }
3243
- break;
3244
- }
3245
- case "MethodDefinition": {
3246
- if (!state.inType) {
3247
- throw new Error("Method definition outside of type!");
3248
- }
3249
- if (node.params) {
3250
- node.params.forEach((param) => {
3251
- if (param.type == "BinaryExpression") {
3252
- state.traverse(param.right);
3253
- }
3254
- });
3255
- }
3256
- return ["returnType"];
3257
- }
3258
- }
3259
- return null;
3260
- };
3261
- (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
3262
- delete state.pre;
3263
- }
3264
-
3265
3213
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
3266
3214
 
3267
3215
 
@@ -3350,7 +3298,8 @@ function collectClassInfo(state) {
3350
3298
  if (elm.hasInvoke && elm.decls) {
3351
3299
  Object.values(elm.decls).forEach((funcs) => {
3352
3300
  funcs.forEach((f) => {
3353
- if (f.type === "FunctionDeclaration" && !f.isStatic) {
3301
+ if (f.type === "FunctionDeclaration" &&
3302
+ !(f.attributes & StateNodeAttributes.STATIC)) {
3354
3303
  markInvokeClassMethod(f);
3355
3304
  }
3356
3305
  });
@@ -3389,7 +3338,7 @@ function getFileASTs(fnMap) {
3389
3338
  return ok;
3390
3339
  }, true));
3391
3340
  }
3392
- async function analyze(fnMap, barrelList, config) {
3341
+ async function analyze(fnMap, resourcesMap, config) {
3393
3342
  let hasTests = false;
3394
3343
  let markApi = true;
3395
3344
  const preState = {
@@ -3430,11 +3379,6 @@ async function analyze(fnMap, barrelList, config) {
3430
3379
  const [scope] = state.stack.slice(-1);
3431
3380
  scope.stack = state.stackClone().slice(0, -1);
3432
3381
  if (scope.type == "FunctionDeclaration") {
3433
- scope.isStatic =
3434
- scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
3435
- (scope.node.attrs &&
3436
- scope.node.attrs.access &&
3437
- scope.node.attrs.access.includes("static"));
3438
3382
  if (markApi) {
3439
3383
  node.body = null;
3440
3384
  scope.info = getApiFunctionInfo(scope);
@@ -3457,7 +3401,7 @@ async function analyze(fnMap, barrelList, config) {
3457
3401
  return null;
3458
3402
  },
3459
3403
  };
3460
- await getApiMapping(preState, barrelList);
3404
+ await getApiMapping(preState, resourcesMap);
3461
3405
  markApi = false;
3462
3406
  const state = preState;
3463
3407
  await getFileASTs(fnMap);
@@ -3473,17 +3417,53 @@ async function analyze(fnMap, barrelList, config) {
3473
3417
  delete state.shouldExclude;
3474
3418
  delete state.pre;
3475
3419
  collectClassInfo(state);
3420
+ state.exposed = state.nextExposed;
3421
+ state.nextExposed = {};
3422
+ return state;
3423
+ }
3424
+ function reportMissingSymbols(state, config) {
3476
3425
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
3477
3426
  ? config?.checkInvalidSymbols || "WARNING"
3478
3427
  : null;
3428
+ const compiler2DiagnosticType = config?.checkCompilerLookupRules !== "OFF"
3429
+ ? config?.checkCompilerLookupRules || "WARNING"
3430
+ : null;
3479
3431
  if (diagnosticType &&
3480
3432
  !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
3481
3433
  const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
3482
- Object.entries(fnMap).forEach(([, v]) => {
3434
+ Object.entries(state.fnMap).forEach(([, v]) => {
3483
3435
  visitReferences(state, v.ast, null, false, (node, results, error) => {
3484
- if (!error)
3436
+ if (node.type === "BinaryExpression" && node.operator === "has") {
3437
+ // Its not an error to check whether a property exists...
3485
3438
  return undefined;
3439
+ }
3486
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
+ }
3487
3467
  if (state.inType) {
3488
3468
  if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
3489
3469
  return undefined;
@@ -3494,9 +3474,6 @@ async function analyze(fnMap, barrelList, config) {
3494
3474
  });
3495
3475
  });
3496
3476
  }
3497
- state.exposed = state.nextExposed;
3498
- state.nextExposed = {};
3499
- return state;
3500
3477
  }
3501
3478
  function compareLiteralLike(a, b) {
3502
3479
  while (a.type === "BinaryExpression")
@@ -3700,7 +3677,7 @@ function optimizeNode(state, node) {
3700
3677
  left.value === null ||
3701
3678
  ((left_type === "Number" || left_type === "Long") &&
3702
3679
  (left.value === 0 || left.value === 0n));
3703
- if (falsy === (node.operator === "&&")) {
3680
+ if (falsy === (node.operator === "&&" || node.operator === "and")) {
3704
3681
  return left;
3705
3682
  }
3706
3683
  if (left_type !== "Boolean" &&
@@ -3710,10 +3687,12 @@ function optimizeNode(state, node) {
3710
3687
  }
3711
3688
  const [right, right_type] = getNodeValue(node.right);
3712
3689
  if (right && right_type === left_type) {
3713
- if (left_type === "Boolean" || node.operator === "||") {
3690
+ if (left_type === "Boolean" ||
3691
+ node.operator === "||" ||
3692
+ node.operator === "or") {
3714
3693
  return right;
3715
3694
  }
3716
- if (node.operator !== "&&") {
3695
+ if (node.operator !== "&&" && node.operator !== "and") {
3717
3696
  throw new Error(`Unexpected operator "${node.operator}"`);
3718
3697
  }
3719
3698
  return { ...node, type: "BinaryExpression", operator: "&" };
@@ -3790,8 +3769,8 @@ function markFunctionCalled(state, func) {
3790
3769
  }
3791
3770
  pushUnique(state.calledFunctions[func.id.name], func);
3792
3771
  }
3793
- async function optimizeMonkeyC(fnMap, barrelList, config) {
3794
- const state = (await analyze(fnMap, barrelList, config));
3772
+ async function optimizeMonkeyC(fnMap, resourcesMap, config) {
3773
+ const state = (await analyze(fnMap, resourcesMap, config));
3795
3774
  state.localsStack = [{}];
3796
3775
  state.calledFunctions = {};
3797
3776
  state.usedByName = {};
@@ -4191,6 +4170,36 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4191
4170
  return replace(optimizeCall(state, node.argument, node), node.argument);
4192
4171
  }
4193
4172
  break;
4173
+ case "BinaryExpression":
4174
+ if (node.operator === "has" &&
4175
+ node.right.type === "UnaryExpression" &&
4176
+ node.right.operator === ":") {
4177
+ const [, results] = state.lookup(node.left);
4178
+ if (results &&
4179
+ results.length === 1 &&
4180
+ results[0].results.length === 1) {
4181
+ const obj = results[0].results[0];
4182
+ if ((obj.type === "ModuleDeclaration" ||
4183
+ obj.type === "Program" ||
4184
+ obj.type === "ClassDeclaration") &&
4185
+ obj.stack) {
4186
+ const exists = hasProperty(obj.decls, node.right.argument.name) ||
4187
+ // This is overkill, since we've already looked up
4188
+ // node.left, but the actual lookup rules are complicated,
4189
+ // and embedded within state.lookup; so just defer to that.
4190
+ state.lookup({
4191
+ type: "MemberExpression",
4192
+ object: node.left,
4193
+ property: node.right.argument,
4194
+ computed: false,
4195
+ })[1];
4196
+ if (!exists) {
4197
+ return replace({ type: "Literal", value: false, raw: "false" }, node);
4198
+ }
4199
+ }
4200
+ }
4201
+ }
4202
+ break;
4194
4203
  case "NewExpression":
4195
4204
  if (state.currentFunction) {
4196
4205
  const [, results] = state.lookup(node.callee);
@@ -4273,7 +4282,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4273
4282
  }
4274
4283
  }
4275
4284
  else {
4276
- const ret = unused(node.expression, true);
4285
+ const ret = unused(state, node.expression, true);
4277
4286
  if (ret) {
4278
4287
  return ret
4279
4288
  .map((r) => replace(r, r))
@@ -4418,7 +4427,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4418
4427
  }
4419
4428
  return null;
4420
4429
  };
4421
- Object.entries(fnMap).forEach(([name, f]) => {
4430
+ Object.entries(fnMap).forEach(([, f]) => {
4422
4431
  traverseAst(f.ast, undefined, (node) => {
4423
4432
  const ret = cleanup(node);
4424
4433
  if (ret === false) {
@@ -4426,6 +4435,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4426
4435
  }
4427
4436
  return ret;
4428
4437
  });
4438
+ });
4439
+ reportMissingSymbols(state, config);
4440
+ Object.entries(fnMap).forEach(([name, f]) => {
4429
4441
  if (state.config && state.config.checkBuildPragmas) {
4430
4442
  pragmaChecker(state, f.ast, state.diagnostics?.[name]);
4431
4443
  }
@@ -4541,6 +4553,322 @@ const negativeFixups = [
4541
4553
 
4542
4554
  ;// CONCATENATED MODULE: external "./sdk-util.cjs"
4543
4555
  const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4556
+ ;// CONCATENATED MODULE: ./src/resources.ts
4557
+
4558
+
4559
+ /*
4560
+ * This is unavoidably ad-hoc. Garmin has arbitrary rules for how
4561
+ * resources can be nested, which we need to mimic here.
4562
+ */
4563
+ function visit_resources(elements, parent, visitor, error) {
4564
+ elements.forEach((e) => {
4565
+ switch (e.name) {
4566
+ // <resources> can contain any of the resource lists (except
4567
+ // another resources), and any of their contents
4568
+ case "resources":
4569
+ if (parent) {
4570
+ error(e, parent);
4571
+ return;
4572
+ }
4573
+ visit_resources(external_sdk_util_cjs_namespaceObject.xmlUtil.elementKids(e), "resources", visitor, error);
4574
+ return;
4575
+ // Each of these is a list that can contain certain kinds of resource.
4576
+ // They can only occur at the top level, or under a <resources> list.
4577
+ case "strings":
4578
+ case "fonts":
4579
+ case "animations":
4580
+ case "bitmaps":
4581
+ case "layouts":
4582
+ case "menus":
4583
+ case "drawables":
4584
+ case "properties":
4585
+ case "settings":
4586
+ case "fitContributions":
4587
+ case "jsonDataResources":
4588
+ case "complications":
4589
+ if (parent && parent !== "resources") {
4590
+ error(e, parent);
4591
+ return;
4592
+ }
4593
+ visit_resources(external_sdk_util_cjs_namespaceObject.xmlUtil.elementKids(e), e.name, visitor, error);
4594
+ return;
4595
+ // These are the resources themselves. Some can occur at top level; most
4596
+ // are restricted to <resources> or one or more of the specific lists above
4597
+ case "string":
4598
+ if (parent !== "strings" && parent !== "resources") {
4599
+ error(e, parent);
4600
+ return;
4601
+ }
4602
+ visitor(e, "Strings");
4603
+ return;
4604
+ case "font":
4605
+ if (parent !== "fonts" && parent !== "resources") {
4606
+ error(e, parent);
4607
+ return;
4608
+ }
4609
+ visitor(e, "Fonts");
4610
+ return;
4611
+ case "animation":
4612
+ if (parent !== "animations" && parent !== "resources") {
4613
+ error(e, parent);
4614
+ return;
4615
+ }
4616
+ visitor(e, "Drawables");
4617
+ return;
4618
+ case "menu":
4619
+ case "menu2":
4620
+ case "checkbox-menu":
4621
+ case "action-menu":
4622
+ if (parent && parent !== "menus" && parent !== "resources") {
4623
+ error(e, parent);
4624
+ return;
4625
+ }
4626
+ visitor(e, "Menus");
4627
+ return;
4628
+ case "bitmap":
4629
+ if (parent !== "bitmaps" &&
4630
+ parent !== "drawables" &&
4631
+ parent !== "resources") {
4632
+ error(e, parent);
4633
+ return;
4634
+ }
4635
+ visitor(e, "Drawables");
4636
+ return;
4637
+ case "layout":
4638
+ if (parent && parent !== "layouts" && parent !== "resources") {
4639
+ error(e, parent);
4640
+ return;
4641
+ }
4642
+ visitor(e, "Layouts");
4643
+ return;
4644
+ case "drawable-list":
4645
+ if (parent && parent !== "drawables" && parent !== "resources") {
4646
+ error(e, parent);
4647
+ return;
4648
+ }
4649
+ visitor(e, "Drawables");
4650
+ return;
4651
+ case "property":
4652
+ if (parent !== "properties" && parent !== "resources") {
4653
+ error(e, parent);
4654
+ return;
4655
+ }
4656
+ visitor(e, "Properties");
4657
+ return;
4658
+ case "setting":
4659
+ if (parent !== "settings" && parent !== "resources") {
4660
+ error(e, parent);
4661
+ return;
4662
+ }
4663
+ visitor(e, null);
4664
+ return;
4665
+ case "group":
4666
+ if (parent !== "settings" /* && parent !== "resources" */) {
4667
+ error(e, parent);
4668
+ return;
4669
+ }
4670
+ visitor(e, null);
4671
+ return;
4672
+ case "fitField":
4673
+ if (parent !== "fitContributions" && parent !== "resources") {
4674
+ error(e, parent);
4675
+ return;
4676
+ }
4677
+ visitor(e, null);
4678
+ return;
4679
+ case "jsonData":
4680
+ if (parent && parent != "jsonDataResources" && parent !== "resources") {
4681
+ error(e, parent);
4682
+ return;
4683
+ }
4684
+ visitor(e, "JsonData");
4685
+ return;
4686
+ case "build":
4687
+ if (parent && parent !== "resources") {
4688
+ error(e, parent);
4689
+ return;
4690
+ }
4691
+ visitor(e, null);
4692
+ return;
4693
+ }
4694
+ });
4695
+ }
4696
+ function add_resources_to_ast(ast, resources) {
4697
+ Object.entries(resources).forEach(([barrel, resourceMap]) => {
4698
+ const rezModules = {
4699
+ Drawables: [],
4700
+ Fonts: [],
4701
+ JsonData: [],
4702
+ Layouts: [],
4703
+ Menus: [],
4704
+ Properties: [],
4705
+ Strings: [],
4706
+ };
4707
+ Object.values(resourceMap).forEach((rez) => {
4708
+ if (!rez || !(rez instanceof external_sdk_util_cjs_namespaceObject.xmlUtil.Document))
4709
+ return;
4710
+ visit_resources(rez.body.elements, null, (e, s) => {
4711
+ if (!s)
4712
+ return;
4713
+ if (!ast_hasProperty(rezModules, s))
4714
+ return;
4715
+ rezModules[s].push(e);
4716
+ }, (_e, _s) => {
4717
+ return;
4718
+ });
4719
+ });
4720
+ const outerLoc = ast.loc && { ...ast.loc };
4721
+ const makeModule = (m) => ({
4722
+ type: "ModuleDeclaration",
4723
+ id: { type: "Identifier", name: m },
4724
+ body: { type: "BlockStatement", body: [] },
4725
+ loc: outerLoc,
4726
+ });
4727
+ let body = ast.body;
4728
+ if (barrel !== "") {
4729
+ const module = makeModule(barrel);
4730
+ body.push(module);
4731
+ body = module.body.body;
4732
+ }
4733
+ const rez = makeModule("Rez");
4734
+ body.push(rez);
4735
+ body = rez.body.body;
4736
+ Object.entries(rezModules).forEach(([m, elements]) => {
4737
+ const module = makeModule(m);
4738
+ body.push(module);
4739
+ elements.forEach((e) => e.attr.id &&
4740
+ module.body.body.push({
4741
+ type: "VariableDeclaration",
4742
+ declarations: [
4743
+ {
4744
+ type: "VariableDeclarator",
4745
+ kind: "var",
4746
+ id: { type: "Identifier", name: e.attr.id, loc: e.loc },
4747
+ loc: e.loc,
4748
+ },
4749
+ ],
4750
+ kind: "var",
4751
+ loc: e.loc,
4752
+ }));
4753
+ });
4754
+ });
4755
+ }
4756
+
4757
+ ;// CONCATENATED MODULE: ./src/visitor.ts
4758
+
4759
+ function visitorNode(node) {
4760
+ if (node.type === "Identifier") {
4761
+ return node;
4762
+ }
4763
+ if (node.type === "MemberExpression") {
4764
+ return node.property;
4765
+ }
4766
+ if (node.type === "BinaryExpression" &&
4767
+ node.operator === "has" &&
4768
+ node.right.type === "UnaryExpression" &&
4769
+ node.right.operator === ":") {
4770
+ return node.right.argument;
4771
+ }
4772
+ return node;
4773
+ }
4774
+ function visitor_visitReferences(state, ast, name, defn, callback) {
4775
+ const checkResults = ([name, results], node) => {
4776
+ if (name && results) {
4777
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
4778
+ if (callback(node, results, false) === false) {
4779
+ return [];
4780
+ }
4781
+ }
4782
+ }
4783
+ else if (defn === false) {
4784
+ if (callback(node, [], results === null) === false) {
4785
+ return [];
4786
+ }
4787
+ }
4788
+ return null;
4789
+ };
4790
+ state.pre = (node) => {
4791
+ switch (node.type) {
4792
+ case "AttributeList":
4793
+ return [];
4794
+ case "UnaryExpression":
4795
+ // a bare symbol isn't a reference
4796
+ if (node.operator === ":")
4797
+ return [];
4798
+ break;
4799
+ case "BinaryExpression":
4800
+ /*
4801
+ * `expr has :symbol` can be treated as a reference
4802
+ * to expr.symbol.
4803
+ */
4804
+ if (node.operator === "has") {
4805
+ if (node.right.type === "UnaryExpression" &&
4806
+ node.right.operator === ":") {
4807
+ if (!name || node.right.argument.name === name) {
4808
+ return checkResults(state.lookup({
4809
+ type: "MemberExpression",
4810
+ object: node.left,
4811
+ property: node.right.argument,
4812
+ computed: false,
4813
+ }), node);
4814
+ }
4815
+ }
4816
+ }
4817
+ break;
4818
+ case "CallExpression":
4819
+ // A call expression whose callee is an identifier is looked
4820
+ // up as a non-local. ie even if there's a same named local,
4821
+ // it will be ignored, and the lookup will start as if the
4822
+ // call had been written self.foo() rather than foo().
4823
+ if (node.callee.type === "Identifier") {
4824
+ if (!name || node.callee.name === name) {
4825
+ /* ignore return value */
4826
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
4827
+ }
4828
+ return ["arguments"];
4829
+ }
4830
+ break;
4831
+ case "Identifier":
4832
+ if (!name || node.name === name) {
4833
+ return checkResults(state.lookup(node), node);
4834
+ }
4835
+ break;
4836
+ case "MemberExpression": {
4837
+ const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
4838
+ if (property) {
4839
+ if (!name || property.name === name) {
4840
+ return checkResults(state.lookup(node), node) || ["object"];
4841
+ }
4842
+ return ["object"];
4843
+ }
4844
+ break;
4845
+ }
4846
+ case "MethodDefinition": {
4847
+ if (!state.inType) {
4848
+ throw new Error("Method definition outside of type!");
4849
+ }
4850
+ if (node.params) {
4851
+ node.params.forEach((param) => {
4852
+ if (param.type == "BinaryExpression") {
4853
+ state.traverse(param.right);
4854
+ }
4855
+ });
4856
+ }
4857
+ return ["returnType"];
4858
+ }
4859
+ case "ModuleDeclaration":
4860
+ return ["body"];
4861
+ case "ClassDeclaration":
4862
+ return ["body", "superClass"];
4863
+ case "FunctionDeclaration":
4864
+ return ["params", "returnType", "body"];
4865
+ }
4866
+ return null;
4867
+ };
4868
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
4869
+ delete state.pre;
4870
+ }
4871
+
4544
4872
  ;// CONCATENATED MODULE: ./src/api.ts
4545
4873
 
4546
4874
 
@@ -4553,6 +4881,8 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4553
4881
 
4554
4882
 
4555
4883
 
4884
+
4885
+
4556
4886
  /*
4557
4887
  * This is an unfortunate hack. I want to be able to extract things
4558
4888
  * like the types of all of a Class's variables (in particular the type
@@ -4562,35 +4892,62 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4562
4892
  * undocumented. The same could be said of compiler.json and simulator.json,
4563
4893
  * but those are at least in a standard format.
4564
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
+ }
4565
4925
  // Extract all enum values from api.mir
4566
- async function api_getApiMapping(state, barrelList) {
4926
+ async function api_getApiMapping(state, resourcesMap) {
4567
4927
  // get the path to the currently active sdk
4568
4928
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
4569
4929
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
4570
- const rezDecl = `module Rez { ${[
4571
- "Drawables",
4572
- "Fonts",
4573
- "JsonData",
4574
- "Layouts",
4575
- "Menus",
4576
- "Strings",
4577
- ]
4578
- .map((s) => ` module ${s} {}\n`)
4579
- .join("")}}`;
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
+ }
4580
4937
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
4581
4938
  .toString()
4582
4939
  .replace(/\r\n/g, "\n")
4583
4940
  .replace(/^\s*\[.*?\]\s*$/gm, "")
4584
4941
  //.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
4585
- .replace(/^(\s*type)\s/gm, "$1def ") +
4586
- (barrelList || [])
4587
- .map((name) => `module ${name} { ${rezDecl} }`)
4588
- .concat(rezDecl)
4589
- .join("");
4942
+ .replace(/^(\s*type)\s/gm, "$1def ");
4590
4943
  try {
4591
- const result = api_collectNamespaces(parser.parse(api, null, {
4944
+ const ast = parser.parse(api, null, {
4592
4945
  filepath: "api.mir",
4593
- }), state);
4946
+ });
4947
+ if (resourcesMap) {
4948
+ add_resources_to_ast(ast, resourcesMap);
4949
+ }
4950
+ const result = api_collectNamespaces(ast, state);
4594
4951
  negativeFixups.forEach((fixup) => {
4595
4952
  const vs = fixup.split(".").reduce((state, part) => {
4596
4953
  const decls = api_isStateNode(state) && state.decls?.[part];
@@ -4633,6 +4990,26 @@ function api_isStateNode(node) {
4633
4990
  function api_variableDeclarationName(node) {
4634
4991
  return ("left" in node ? node.left : node).name;
4635
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
+ }
4636
5013
  function lookupToStateNodeDecls(results) {
4637
5014
  return results.reduce((result, current) => current.results.length
4638
5015
  ? result
@@ -4640,7 +5017,7 @@ function lookupToStateNodeDecls(results) {
4640
5017
  : current.results
4641
5018
  : result, null);
4642
5019
  }
4643
- function checkOne(state, ns, decls, node, isStatic) {
5020
+ function checkOne(state, ns, decls, node) {
4644
5021
  // follow the superchain, looking up node in each class
4645
5022
  const superChain = (cls) => {
4646
5023
  if (!cls.superClass || cls.superClass === true) {
@@ -4655,7 +5032,7 @@ function checkOne(state, ns, decls, node, isStatic) {
4655
5032
  }, null);
4656
5033
  };
4657
5034
  const lookupInContext = (ns) => {
4658
- const [, lkup] = lookup(state, decls, node, null, ns.stack);
5035
+ const [, lkup] = lookup(state, decls, node, null, ns.stack, false, true);
4659
5036
  return lkup && lookupToStateNodeDecls(lkup);
4660
5037
  };
4661
5038
  // follow the superchain, looking up node in each class's scope
@@ -4677,10 +5054,7 @@ function checkOne(state, ns, decls, node, isStatic) {
4677
5054
  }
4678
5055
  switch (ns.type) {
4679
5056
  case "ClassDeclaration":
4680
- if (!isStatic) {
4681
- return superChain(ns) || superChainScopes(ns) || false;
4682
- }
4683
- // fall through
5057
+ return superChain(ns) || superChainScopes(ns) || false;
4684
5058
  case "ModuleDeclaration":
4685
5059
  return lookupInContext(ns) || false;
4686
5060
  }
@@ -4728,7 +5102,7 @@ function api_isLookupCandidate(node) {
4728
5102
  * - [false, false] - if the lookup fails, but its not an error because its not the kind of expression we can lookup
4729
5103
  * - [null, null] - if the lookup fails unexpectedly.
4730
5104
  */
4731
- function lookup(state, decls, node, name, maybeStack, nonlocal) {
5105
+ function lookup(state, decls, node, name, maybeStack, nonlocal, ignoreImports) {
4732
5106
  const stack = maybeStack || state.stack;
4733
5107
  switch (node.type) {
4734
5108
  case "MemberExpression": {
@@ -4737,7 +5111,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4737
5111
  break;
4738
5112
  let result;
4739
5113
  if (node.object.type === "ThisExpression") {
4740
- [, result] = lookup(state, decls, node.property, name, stack, true);
5114
+ [, result] = lookup(state, decls, node.property, name, stack, true, true);
4741
5115
  }
4742
5116
  else {
4743
5117
  const [, results] = lookup(state, decls, node.object, name, stack, false);
@@ -4751,7 +5125,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4751
5125
  if (!api_isStateNode(module)) {
4752
5126
  return null;
4753
5127
  }
4754
- const res = checkOne(state, module, decls, property, false);
5128
+ const res = checkOne(state, module, decls, property);
4755
5129
  return res ? { parent: module, results: res } : null;
4756
5130
  })
4757
5131
  .filter((r) => r != null);
@@ -4760,14 +5134,11 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4760
5134
  return current ? current.concat(items) : items;
4761
5135
  }, null);
4762
5136
  if (!result &&
4763
- results.some((ld) => ld.results.some((sn) => (api_isStateNode(sn) && sn.fullName?.match(/^\$\.(\w+\.)?Rez\./)) ||
4764
- sn.type === "VariableDeclarator" ||
5137
+ results.some((ld) => ld.results.some((sn) => sn.type === "VariableDeclarator" ||
4765
5138
  sn.type === "Identifier" ||
4766
5139
  sn.type === "BinaryExpression" ||
4767
5140
  (sn.type === "ClassDeclaration" &&
4768
5141
  property.name === "initialize")))) {
4769
- // - The Rez module can contain lots of things from the resource
4770
- // compiler which we don't track.
4771
5142
  // - Variables, and formal parameters would require type tracking
4772
5143
  // which we don't yet do
4773
5144
  // - Its ok to call an undeclared initialize method.
@@ -4781,7 +5152,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4781
5152
  }
4782
5153
  case "ThisExpression": {
4783
5154
  for (let i = stack.length;;) {
4784
- const si = stack[i];
5155
+ const si = stack[--i];
4785
5156
  if (si.type == "ModuleDeclaration" ||
4786
5157
  si.type == "ClassDeclaration" ||
4787
5158
  !i) {
@@ -4797,30 +5168,66 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4797
5168
  return [name || node.name, [{ parent: null, results: [stack[0]] }]];
4798
5169
  }
4799
5170
  let inStatic = false;
4800
- let checkedImports = false;
5171
+ let checkedImports = ignoreImports;
5172
+ let imports = null;
4801
5173
  for (let i = stack.length; i--;) {
4802
5174
  const si = stack[i];
4803
5175
  switch (si.type) {
4804
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
4805
5203
  case "ModuleDeclaration":
4806
5204
  case "Program":
4807
5205
  if (!checkedImports) {
4808
5206
  checkedImports = true;
4809
- const results = findUsingForNode(state, stack, i, node, decls === "type_decls");
5207
+ const results = findUsingForNode(state, stack, i, node);
4810
5208
  if (results) {
4811
- 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
+ }
4812
5219
  }
4813
5220
  }
4814
5221
  break;
4815
5222
  case "FunctionDeclaration":
4816
- inStatic = si.isStatic === true;
5223
+ inStatic = !!(si.attributes & optimizer_types_StateNodeAttributes.STATIC);
4817
5224
  // fall through
4818
5225
  default:
4819
5226
  if (nonlocal)
4820
5227
  continue;
4821
5228
  break;
4822
5229
  }
4823
- const results = checkOne(state, si, decls, node, inStatic);
5230
+ const results = checkOne(state, si, decls, node);
4824
5231
  if (results) {
4825
5232
  return [name || node.name, [{ parent: si, results }]];
4826
5233
  }
@@ -4828,6 +5235,31 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4828
5235
  break;
4829
5236
  }
4830
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
+ }
4831
5263
  return [null, null];
4832
5264
  }
4833
5265
  }
@@ -4841,9 +5273,27 @@ function api_collectNamespaces(ast, stateIn) {
4841
5273
  state.index = {};
4842
5274
  if (!state.stack) {
4843
5275
  state.stack = [
4844
- { type: "Program", name: "$", fullName: "$", node: undefined },
5276
+ {
5277
+ type: "Program",
5278
+ name: "$",
5279
+ fullName: "$",
5280
+ node: undefined,
5281
+ attributes: 0,
5282
+ },
4845
5283
  ];
4846
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
+ }
4847
5297
  state.removeNodeComments = (node, ast) => {
4848
5298
  if (node.start && node.end && ast.comments && ast.comments.length) {
4849
5299
  let low = 0, high = ast.comments.length;
@@ -4938,6 +5388,7 @@ function api_collectNamespaces(ast, stateIn) {
4938
5388
  name: undefined,
4939
5389
  node: node.body,
4940
5390
  decls: { [id.name]: [id] },
5391
+ attributes: 0,
4941
5392
  });
4942
5393
  }
4943
5394
  break;
@@ -4948,6 +5399,7 @@ function api_collectNamespaces(ast, stateIn) {
4948
5399
  fullName: undefined,
4949
5400
  name: undefined,
4950
5401
  node: node,
5402
+ attributes: 0,
4951
5403
  });
4952
5404
  }
4953
5405
  break;
@@ -4975,6 +5427,9 @@ function api_collectNamespaces(ast, stateIn) {
4975
5427
  name,
4976
5428
  fullName,
4977
5429
  node,
5430
+ attributes: node.type === "BlockStatement"
5431
+ ? 0
5432
+ : stateNodeAttrs(node.attrs),
4978
5433
  };
4979
5434
  state.stack.push(elm);
4980
5435
  if (name) {
@@ -5001,9 +5456,6 @@ function api_collectNamespaces(ast, stateIn) {
5001
5456
  parent.decls[name].push(elm);
5002
5457
  if (node.type == "ModuleDeclaration" ||
5003
5458
  node.type == "ClassDeclaration") {
5004
- // Inject the class/module name into itself,
5005
- // so you can say Graphics.Graphics.Graphics.COLOR_RED
5006
- elm.decls = { [name]: [elm] };
5007
5459
  if (!parent.type_decls)
5008
5460
  parent.type_decls = {};
5009
5461
  if (!ast_hasProperty(parent.type_decls, name)) {
@@ -5045,6 +5497,7 @@ function api_collectNamespaces(ast, stateIn) {
5045
5497
  node,
5046
5498
  name,
5047
5499
  fullName: parent.fullName + "." + name,
5500
+ attributes: stateNodeAttrs(node.attrs),
5048
5501
  });
5049
5502
  break;
5050
5503
  }
@@ -5063,12 +5516,13 @@ function api_collectNamespaces(ast, stateIn) {
5063
5516
  return;
5064
5517
  }
5065
5518
  decl.kind = node.kind;
5066
- (0,external_util_cjs_namespaceObject.pushUnique)(decls[name], {
5519
+ decls[name].push({
5067
5520
  type: "VariableDeclarator",
5068
5521
  node: decl,
5069
5522
  name,
5070
5523
  fullName: parent.fullName + "." + name,
5071
5524
  stack,
5525
+ attributes: stateNodeAttrs(node.attrs),
5072
5526
  });
5073
5527
  if (node.kind == "const") {
5074
5528
  if (!ast_hasProperty(state.index, name)) {
@@ -5293,27 +5747,32 @@ function findUsing(state, stack, using) {
5293
5747
  }
5294
5748
  return null;
5295
5749
  }
5296
- function findUsingForNode(state, stack, i, node, isType) {
5750
+ function findUsingForNode(state, stack, i, node) {
5751
+ let imports = null;
5297
5752
  while (i >= 0) {
5298
5753
  const si = stack[i--];
5299
5754
  if (ast_hasProperty(si.usings, node.name)) {
5300
5755
  const using = si.usings[node.name];
5301
- const module = findUsing(state, stack, using);
5302
- return module && [module];
5756
+ return findUsing(state, stack, using);
5303
5757
  }
5304
- if (si.imports && isType) {
5758
+ if (si.imports) {
5305
5759
  for (let j = si.imports.length; j--;) {
5306
5760
  const using = si.imports[j];
5307
5761
  const module = findUsing(state, stack, using);
5308
5762
  if (module) {
5309
5763
  if (ast_hasProperty(module.type_decls, node.name)) {
5310
- 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
+ });
5311
5770
  }
5312
5771
  }
5313
5772
  }
5314
5773
  }
5315
5774
  }
5316
- return null;
5775
+ return imports;
5317
5776
  }
5318
5777
  const invokeInfo = {};
5319
5778
  const toyboxFnInfo = {};