@markw65/monkeyc-optimizer 1.0.37 → 1.0.39

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 = {collectNamespaces,findUsingForNode,formatAst,getApiFunctionInfo,getApiMapping,hasProperty,isLookupCandidate,isStateNode,markInvokeClassMethod,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
  /**
@@ -270,7 +270,8 @@ __webpack_require__.d(__webpack_exports__, {
270
270
  "sameLookupResult": () => (/* binding */ api_sameLookupResult),
271
271
  "traverseAst": () => (/* reexport */ ast_traverseAst),
272
272
  "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
273
- "visitReferences": () => (/* reexport */ visitor_visitReferences)
273
+ "visitReferences": () => (/* reexport */ visitor_visitReferences),
274
+ "visitorNode": () => (/* reexport */ visitorNode)
274
275
  });
275
276
 
276
277
  ;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
@@ -655,8 +656,13 @@ function function_info_findCalleesForNew(lookupDefs) {
655
656
 
656
657
  function variable_renamer_renameVariable(state, locals, declName) {
657
658
  const map = locals.map;
658
- if (!hasProperty(map, declName))
659
- return null;
659
+ if (declName) {
660
+ if (!hasProperty(map, declName))
661
+ return null;
662
+ }
663
+ else {
664
+ declName = "tmp";
665
+ }
660
666
  let suffix = 0;
661
667
  let node_name = declName;
662
668
  const match = node_name.match(/^pmcr_(.*)_(\d+)$/);
@@ -711,6 +717,69 @@ function variable_renamer_renameVariable(state, locals, declName) {
711
717
 
712
718
 
713
719
 
720
+ // Note: Keep in sync with replaceInlinedSubExpression below
721
+ function inliner_inlinableSubExpression(expr) {
722
+ while (true) {
723
+ if (expr.type === "BinaryExpression" || expr.type === "LogicalExpression") {
724
+ expr = expr.left;
725
+ }
726
+ else if (expr.type === "UnaryExpression") {
727
+ expr = expr.argument;
728
+ }
729
+ else if (expr.type === "ConditionalExpression") {
730
+ expr = expr.test;
731
+ }
732
+ else if (expr.type === "MemberExpression") {
733
+ expr = expr.object;
734
+ }
735
+ else if (expr.type === "CallExpression") {
736
+ return expr;
737
+ }
738
+ else {
739
+ return null;
740
+ }
741
+ }
742
+ }
743
+ // Note: Keep in sync with inlinableSubExpression above
744
+ function replaceInlinedSubExpression(top, call, repl) {
745
+ if (top === call)
746
+ return repl;
747
+ let expr = top;
748
+ while (true) {
749
+ if (expr.type === "LogicalExpression" || expr.type === "BinaryExpression") {
750
+ if (expr.left === call) {
751
+ expr.left = repl;
752
+ break;
753
+ }
754
+ expr = expr.left;
755
+ }
756
+ else if (expr.type === "UnaryExpression") {
757
+ if (expr.argument === call) {
758
+ expr.argument = repl;
759
+ break;
760
+ }
761
+ expr = expr.argument;
762
+ }
763
+ else if (expr.type === "ConditionalExpression") {
764
+ if (expr.test === call) {
765
+ expr.test = repl;
766
+ break;
767
+ }
768
+ expr = expr.test;
769
+ }
770
+ else if (expr.type === "MemberExpression") {
771
+ if (expr.object === call) {
772
+ expr.object = repl;
773
+ break;
774
+ }
775
+ expr = expr.object;
776
+ }
777
+ else {
778
+ throw new Error("Internal error: Didn't find CallExpression");
779
+ }
780
+ }
781
+ return top;
782
+ }
714
783
  function getArgSafety(state, func, args, requireAll) {
715
784
  // determine whether decl might be changed by a function call
716
785
  // or assignment during the evaluation of FunctionStateNode.
@@ -938,7 +1007,7 @@ function inliner_shouldInline(state, func, call, context) {
938
1007
  }
939
1008
  }
940
1009
  if (!context && requested) {
941
- inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, or return contexts");
1010
+ inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, if or return contexts");
942
1011
  }
943
1012
  return context != null;
944
1013
  }
@@ -1137,6 +1206,9 @@ function inliner_unused(expression, top) {
1137
1206
  if (expression.computed) {
1138
1207
  return inliner_unused(expression.object).concat(inliner_unused(expression.property));
1139
1208
  }
1209
+ if (expression.object.type === "NewExpression") {
1210
+ break;
1211
+ }
1140
1212
  return inliner_unused(expression.object);
1141
1213
  case "ArrayExpression":
1142
1214
  return expression.elements.map((e) => inliner_unused(e)).flat(1);
@@ -1198,7 +1270,8 @@ function inlineWithArgs(state, func, call, context) {
1198
1270
  inlineDiagnostic(state, func, call, "Function had more than one return statement");
1199
1271
  }
1200
1272
  else if ((context.type === "AssignmentExpression" ||
1201
- context.type === "VariableDeclarator") &&
1273
+ context.type === "VariableDeclarator" ||
1274
+ context.type === "IfStatement") &&
1202
1275
  retStmtCount !== 1) {
1203
1276
  inlineDiagnostic(state, func, call, "Function did not have a return statement");
1204
1277
  return null;
@@ -1208,7 +1281,8 @@ function inlineWithArgs(state, func, call, context) {
1208
1281
  if (!last ||
1209
1282
  last.type !== "ReturnStatement" ||
1210
1283
  ((context.type === "AssignmentExpression" ||
1211
- context.type === "VariableDeclarator") &&
1284
+ context.type === "VariableDeclarator" ||
1285
+ context.type === "IfStatement") &&
1212
1286
  !last.argument)) {
1213
1287
  inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
1214
1288
  return null;
@@ -1235,14 +1309,15 @@ function inlineWithArgs(state, func, call, context) {
1235
1309
  }
1236
1310
  if (last.argument) {
1237
1311
  if (context.type === "AssignmentExpression") {
1238
- context.right = last.argument;
1312
+ context.right = replaceInlinedSubExpression(context.right, call, last.argument);
1239
1313
  block.body[block.body.length - 1] = {
1240
1314
  type: "ExpressionStatement",
1241
1315
  expression: context,
1242
1316
  };
1243
1317
  }
1244
1318
  else if (context.type === "VariableDeclarator") {
1245
- const { id, init: _init, kind: _kind, ...rest } = context;
1319
+ const { id, init, kind: _kind, ...rest } = context;
1320
+ const right = replaceInlinedSubExpression(init, call, last.argument);
1246
1321
  block.body[block.body.length - 1] = {
1247
1322
  ...rest,
1248
1323
  type: "ExpressionStatement",
@@ -1251,9 +1326,52 @@ function inlineWithArgs(state, func, call, context) {
1251
1326
  type: "AssignmentExpression",
1252
1327
  operator: "=",
1253
1328
  left: id.type === "Identifier" ? id : id.left,
1329
+ right,
1330
+ },
1331
+ };
1332
+ }
1333
+ else if (context.type === "IfStatement") {
1334
+ // Generate a pmcr_tmp name that doesn't conflict with anything
1335
+ const locals = state.localsStack[state.localsStack.length - 1];
1336
+ const name = renameVariable(state, locals, null);
1337
+ locals.map[name] = true;
1338
+ // Replace the inlined function's return statement
1339
+ // with an assignment to pmcr_tmp
1340
+ block.body[block.body.length - 1] = {
1341
+ type: "ExpressionStatement",
1342
+ expression: {
1343
+ type: "AssignmentExpression",
1344
+ operator: "=",
1345
+ left: { type: "Identifier", name },
1254
1346
  right: last.argument,
1255
1347
  },
1256
1348
  };
1349
+ // The IfStatement either has the call as its test, or as
1350
+ // the leftmost argument to a series of Binary/Logical expressions
1351
+ // Either way, replace the call with pmcr_tmp
1352
+ const repl = { type: "Identifier", name };
1353
+ context.test = replaceInlinedSubExpression(context.test, call, repl);
1354
+ // Wrap the inlined body so it looks like
1355
+ // {
1356
+ // var pmcr_tmp;
1357
+ // { /* inlined body, with assignment to pmcr_tmp */ }
1358
+ // if (context) {} // original if statement
1359
+ // }
1360
+ body.body = [
1361
+ {
1362
+ type: "VariableDeclaration",
1363
+ kind: "var",
1364
+ declarations: [
1365
+ {
1366
+ type: "VariableDeclarator",
1367
+ kind: "var",
1368
+ id: { type: "Identifier", name },
1369
+ },
1370
+ ],
1371
+ },
1372
+ { type: "BlockStatement", body: body.body },
1373
+ context,
1374
+ ];
1257
1375
  }
1258
1376
  else {
1259
1377
  const side_exprs = inliner_unused(last.argument);
@@ -1427,7 +1545,7 @@ function pragma_checker_pragmaChecker(state, ast, diagnostics) {
1427
1545
  if (quote == '"') {
1428
1546
  return haystack.includes(needle);
1429
1547
  }
1430
- const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
1548
+ const re = new RegExp(needle.replace(/@([\d\w]+)/g, "(pre_)?$1(_\\d+)?"));
1431
1549
  return re.test(haystack);
1432
1550
  };
1433
1551
  next();
@@ -2012,7 +2130,7 @@ function getPreOrder(head) {
2012
2130
  }
2013
2131
 
2014
2132
  // EXTERNAL MODULE: ./node_modules/priorityqueuejs/index.js
2015
- var priorityqueuejs = __webpack_require__(2789);
2133
+ var priorityqueuejs = __webpack_require__(789);
2016
2134
  ;// CONCATENATED MODULE: ./src/pre.ts
2017
2135
 
2018
2136
 
@@ -3005,6 +3123,13 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3005
3123
  if (hasProperty(toRemove, name)) {
3006
3124
  const rep = vdecl.init ? unused(vdecl.init) : [];
3007
3125
  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")))) {
3131
+ continue;
3132
+ }
3008
3133
  if (parent.node.type === "ForStatement") {
3009
3134
  // declarations whose inits have side effects
3010
3135
  // can't be deleted from for statements.
@@ -3054,100 +3179,6 @@ function unused_exprs_cleanupUnusedVars(state, node) {
3054
3179
  }
3055
3180
  }
3056
3181
 
3057
- ;// CONCATENATED MODULE: ./src/visitor.ts
3058
-
3059
- function visitor_visitReferences(state, ast, name, defn, callback) {
3060
- const checkResults = ([name, results], node) => {
3061
- if (name && results) {
3062
- if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
3063
- if (callback(node, results, false) === false) {
3064
- return [];
3065
- }
3066
- }
3067
- }
3068
- else if (defn === false) {
3069
- if (callback(node, [], results === null) === false) {
3070
- return [];
3071
- }
3072
- }
3073
- return null;
3074
- };
3075
- state.pre = (node) => {
3076
- switch (node.type) {
3077
- case "AttributeList":
3078
- return [];
3079
- case "UnaryExpression":
3080
- // a bare symbol isn't a reference
3081
- if (node.operator === ":")
3082
- return [];
3083
- break;
3084
- case "BinaryExpression":
3085
- /*
3086
- * `expr has :symbol` can be treated as a reference
3087
- * to expr.symbol.
3088
- */
3089
- if (node.operator === "has") {
3090
- if (node.right.type === "UnaryExpression" &&
3091
- node.right.operator === ":") {
3092
- if (!name || node.right.argument.name === name) {
3093
- return checkResults(state.lookup({
3094
- type: "MemberExpression",
3095
- object: node.left,
3096
- property: node.right.argument,
3097
- computed: false,
3098
- }), node.right.argument);
3099
- }
3100
- }
3101
- }
3102
- break;
3103
- case "CallExpression":
3104
- // A call expression whose callee is an identifier is looked
3105
- // up as a non-local. ie even if there's a same named local,
3106
- // it will be ignored, and the lookup will start as if the
3107
- // call had been written self.foo() rather than foo().
3108
- if (node.callee.type === "Identifier") {
3109
- if (!name || node.callee.name === name) {
3110
- /* ignore return value */
3111
- checkResults(state.lookupNonlocal(node.callee), node.callee);
3112
- }
3113
- return ["arguments"];
3114
- }
3115
- break;
3116
- case "Identifier":
3117
- if (!name || node.name === name) {
3118
- return checkResults(state.lookup(node), node);
3119
- }
3120
- break;
3121
- case "MemberExpression": {
3122
- const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
3123
- if (property) {
3124
- if (!name || property.name === name) {
3125
- return checkResults(state.lookup(node), node) || ["object"];
3126
- }
3127
- return ["object"];
3128
- }
3129
- break;
3130
- }
3131
- case "MethodDefinition": {
3132
- if (!state.inType) {
3133
- throw new Error("Method definition outside of type!");
3134
- }
3135
- if (node.params) {
3136
- node.params.forEach((param) => {
3137
- if (param.type == "BinaryExpression") {
3138
- state.traverse(param.right);
3139
- }
3140
- });
3141
- }
3142
- return ["returnType"];
3143
- }
3144
- }
3145
- return null;
3146
- };
3147
- (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
3148
- delete state.pre;
3149
- }
3150
-
3151
3182
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
3152
3183
 
3153
3184
 
@@ -3160,7 +3191,6 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
3160
3191
 
3161
3192
 
3162
3193
 
3163
-
3164
3194
  function collectClassInfo(state) {
3165
3195
  const toybox = state.stack[0].decls["Toybox"][0];
3166
3196
  const lang = toybox.decls["Lang"][0];
@@ -3275,7 +3305,7 @@ function getFileASTs(fnMap) {
3275
3305
  return ok;
3276
3306
  }, true));
3277
3307
  }
3278
- async function analyze(fnMap, barrelList, config) {
3308
+ async function analyze(fnMap, resourcesMap, config) {
3279
3309
  let hasTests = false;
3280
3310
  let markApi = true;
3281
3311
  const preState = {
@@ -3343,7 +3373,7 @@ async function analyze(fnMap, barrelList, config) {
3343
3373
  return null;
3344
3374
  },
3345
3375
  };
3346
- await getApiMapping(preState, barrelList);
3376
+ await getApiMapping(preState, resourcesMap);
3347
3377
  markApi = false;
3348
3378
  const state = preState;
3349
3379
  await getFileASTs(fnMap);
@@ -3359,16 +3389,25 @@ async function analyze(fnMap, barrelList, config) {
3359
3389
  delete state.shouldExclude;
3360
3390
  delete state.pre;
3361
3391
  collectClassInfo(state);
3392
+ state.exposed = state.nextExposed;
3393
+ state.nextExposed = {};
3394
+ return state;
3395
+ }
3396
+ function reportMissingSymbols(state, config) {
3362
3397
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
3363
3398
  ? config?.checkInvalidSymbols || "WARNING"
3364
3399
  : null;
3365
3400
  if (diagnosticType &&
3366
3401
  !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
3367
3402
  const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
3368
- Object.entries(fnMap).forEach(([, v]) => {
3403
+ Object.entries(state.fnMap).forEach(([, v]) => {
3369
3404
  visitReferences(state, v.ast, null, false, (node, results, error) => {
3370
3405
  if (!error)
3371
3406
  return undefined;
3407
+ if (node.type === "BinaryExpression" && node.operator === "has") {
3408
+ // Its not an error to check whether a property exists...
3409
+ return undefined;
3410
+ }
3372
3411
  const nodeStr = formatAst(node);
3373
3412
  if (state.inType) {
3374
3413
  if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
@@ -3380,9 +3419,6 @@ async function analyze(fnMap, barrelList, config) {
3380
3419
  });
3381
3420
  });
3382
3421
  }
3383
- state.exposed = state.nextExposed;
3384
- state.nextExposed = {};
3385
- return state;
3386
3422
  }
3387
3423
  function compareLiteralLike(a, b) {
3388
3424
  while (a.type === "BinaryExpression")
@@ -3676,8 +3712,8 @@ function markFunctionCalled(state, func) {
3676
3712
  }
3677
3713
  pushUnique(state.calledFunctions[func.id.name], func);
3678
3714
  }
3679
- async function optimizeMonkeyC(fnMap, barrelList, config) {
3680
- const state = (await analyze(fnMap, barrelList, config));
3715
+ async function optimizeMonkeyC(fnMap, resourcesMap, config) {
3716
+ const state = (await analyze(fnMap, resourcesMap, config));
3681
3717
  state.localsStack = [{}];
3682
3718
  state.calledFunctions = {};
3683
3719
  state.usedByName = {};
@@ -4048,11 +4084,18 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4048
4084
  return false;
4049
4085
  return replace(rep, rep);
4050
4086
  }
4051
- else if (node.type === "IfStatement" &&
4052
- node.alternate &&
4053
- node.alternate.type === "BlockStatement" &&
4054
- !node.alternate.body.length) {
4055
- delete node.alternate;
4087
+ else if (node.type === "IfStatement") {
4088
+ if (node.alternate &&
4089
+ node.alternate.type === "BlockStatement" &&
4090
+ !node.alternate.body.length) {
4091
+ delete node.alternate;
4092
+ }
4093
+ else {
4094
+ const call = inlinableSubExpression(node.test);
4095
+ if (call) {
4096
+ return replace(optimizeCall(state, call, node), node.test);
4097
+ }
4098
+ }
4056
4099
  }
4057
4100
  break;
4058
4101
  case "WhileStatement":
@@ -4070,6 +4113,37 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4070
4113
  return replace(optimizeCall(state, node.argument, node), node.argument);
4071
4114
  }
4072
4115
  break;
4116
+ case "BinaryExpression":
4117
+ if (node.operator === "has" &&
4118
+ node.right.type === "UnaryExpression" &&
4119
+ node.right.operator === ":") {
4120
+ const [, results] = state.lookup(node.left);
4121
+ if (results &&
4122
+ results.length === 1 &&
4123
+ results[0].results.length === 1) {
4124
+ const obj = results[0].results[0];
4125
+ if ((obj.type === "ModuleDeclaration" ||
4126
+ obj.type === "Program" ||
4127
+ obj.type === "ClassDeclaration") &&
4128
+ obj.decls &&
4129
+ obj.stack) {
4130
+ const exists = hasProperty(obj.decls, node.right.argument.name) ||
4131
+ // This is overkill, since we've already looked up
4132
+ // node.left, but the actual lookup rules are complicated,
4133
+ // and embedded within state.lookup; so just defer to that.
4134
+ state.lookup({
4135
+ type: "MemberExpression",
4136
+ object: node.left,
4137
+ property: node.right.argument,
4138
+ computed: false,
4139
+ })[1];
4140
+ if (!exists) {
4141
+ return replace({ type: "Literal", value: false, raw: "false" }, node);
4142
+ }
4143
+ }
4144
+ }
4145
+ }
4146
+ break;
4073
4147
  case "NewExpression":
4074
4148
  if (state.currentFunction) {
4075
4149
  const [, results] = state.lookup(node.callee);
@@ -4095,8 +4169,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4095
4169
  let j = 0;
4096
4170
  while (i < node.declarations.length) {
4097
4171
  const decl = declarations[i++];
4098
- if (decl.init && decl.init.type === "CallExpression") {
4099
- const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
4172
+ if (!decl.init)
4173
+ continue;
4174
+ const call = inlinableSubExpression(decl.init);
4175
+ if (call) {
4176
+ const inlined = replace(optimizeCall(state, call, decl), decl.init);
4100
4177
  if (!inlined)
4101
4178
  continue;
4102
4179
  if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
@@ -4131,7 +4208,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4131
4208
  return replace(optimizeCall(state, node.expression, node), node.expression);
4132
4209
  }
4133
4210
  else if (node.expression.type === "AssignmentExpression") {
4134
- if (node.expression.right.type === "CallExpression") {
4211
+ const call = inlinableSubExpression(node.expression.right);
4212
+ if (call) {
4135
4213
  let ok = false;
4136
4214
  if (node.expression.left.type === "Identifier") {
4137
4215
  if (hasProperty(topLocals().map, node.expression.left.type)) {
@@ -4143,7 +4221,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4143
4221
  ok = !!result;
4144
4222
  }
4145
4223
  if (ok) {
4146
- return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
4224
+ return replace(optimizeCall(state, call, node.expression), node.expression.right);
4147
4225
  }
4148
4226
  }
4149
4227
  }
@@ -4293,7 +4371,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4293
4371
  }
4294
4372
  return null;
4295
4373
  };
4296
- Object.entries(fnMap).forEach(([name, f]) => {
4374
+ Object.entries(fnMap).forEach(([, f]) => {
4297
4375
  traverseAst(f.ast, undefined, (node) => {
4298
4376
  const ret = cleanup(node);
4299
4377
  if (ret === false) {
@@ -4301,6 +4379,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
4301
4379
  }
4302
4380
  return ret;
4303
4381
  });
4382
+ });
4383
+ reportMissingSymbols(state, config);
4384
+ Object.entries(fnMap).forEach(([name, f]) => {
4304
4385
  if (state.config && state.config.checkBuildPragmas) {
4305
4386
  pragmaChecker(state, f.ast, state.diagnostics?.[name]);
4306
4387
  }
@@ -4416,6 +4497,316 @@ const negativeFixups = [
4416
4497
 
4417
4498
  ;// CONCATENATED MODULE: external "./sdk-util.cjs"
4418
4499
  const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4500
+ ;// CONCATENATED MODULE: ./src/resources.ts
4501
+
4502
+
4503
+ /*
4504
+ * This is unavoidably ad-hoc. Garmin has arbitrary rules for how
4505
+ * resources can be nested, which we need to mimic here.
4506
+ */
4507
+ function visit_resources(elements, parent, visitor, error) {
4508
+ elements.forEach((e) => {
4509
+ switch (e.name) {
4510
+ // <resources> can contain any of the resource lists (except
4511
+ // another resources), and any of their contents
4512
+ case "resources":
4513
+ if (parent) {
4514
+ error(e, parent);
4515
+ return;
4516
+ }
4517
+ visit_resources(external_sdk_util_cjs_namespaceObject.xmlUtil.elementKids(e), "resources", visitor, error);
4518
+ return;
4519
+ // Each of these is a list that can contain certain kinds of resource.
4520
+ // They can only occur at the top level, or under a <resources> list.
4521
+ case "strings":
4522
+ case "fonts":
4523
+ case "animations":
4524
+ case "bitmaps":
4525
+ case "layouts":
4526
+ case "menus":
4527
+ case "drawables":
4528
+ case "properties":
4529
+ case "settings":
4530
+ case "fitContributions":
4531
+ case "jsonDataResources":
4532
+ case "complications":
4533
+ if (parent && parent !== "resources") {
4534
+ error(e, parent);
4535
+ return;
4536
+ }
4537
+ visit_resources(external_sdk_util_cjs_namespaceObject.xmlUtil.elementKids(e), e.name, visitor, error);
4538
+ return;
4539
+ // These are the resources themselves. Some can occur at top level; most
4540
+ // are restricted to <resources> or one or more of the specific lists above
4541
+ case "string":
4542
+ if (parent !== "strings" && parent !== "resources") {
4543
+ error(e, parent);
4544
+ return;
4545
+ }
4546
+ visitor(e, "Strings");
4547
+ return;
4548
+ case "font":
4549
+ if (parent !== "fonts" && parent !== "resources") {
4550
+ error(e, parent);
4551
+ return;
4552
+ }
4553
+ visitor(e, "Fonts");
4554
+ return;
4555
+ case "animation":
4556
+ if (parent !== "animations" && parent !== "resources") {
4557
+ error(e, parent);
4558
+ return;
4559
+ }
4560
+ visitor(e, "Drawables");
4561
+ return;
4562
+ case "menu":
4563
+ case "menu2":
4564
+ case "checkbox-menu":
4565
+ case "action-menu":
4566
+ if (parent && parent !== "menus" && parent !== "resources") {
4567
+ error(e, parent);
4568
+ return;
4569
+ }
4570
+ visitor(e, "Menus");
4571
+ return;
4572
+ case "bitmap":
4573
+ if (parent !== "bitmaps" &&
4574
+ parent !== "drawables" &&
4575
+ parent !== "resources") {
4576
+ error(e, parent);
4577
+ return;
4578
+ }
4579
+ visitor(e, "Drawables");
4580
+ return;
4581
+ case "layout":
4582
+ if (parent && parent !== "layouts" && parent !== "resources") {
4583
+ error(e, parent);
4584
+ return;
4585
+ }
4586
+ visitor(e, "Layouts");
4587
+ return;
4588
+ case "drawable-list":
4589
+ if (parent && parent !== "drawables" && parent !== "resources") {
4590
+ error(e, parent);
4591
+ return;
4592
+ }
4593
+ visitor(e, "Drawables");
4594
+ return;
4595
+ case "property":
4596
+ if (parent !== "properties" && parent !== "resources") {
4597
+ error(e, parent);
4598
+ return;
4599
+ }
4600
+ visitor(e, "Properties");
4601
+ return;
4602
+ case "setting":
4603
+ if (parent !== "settings" && parent !== "resources") {
4604
+ error(e, parent);
4605
+ return;
4606
+ }
4607
+ visitor(e, null);
4608
+ return;
4609
+ case "group":
4610
+ if (parent !== "settings" /* && parent !== "resources" */) {
4611
+ error(e, parent);
4612
+ return;
4613
+ }
4614
+ visitor(e, null);
4615
+ return;
4616
+ case "fitField":
4617
+ if (parent !== "fitContributions" && parent !== "resources") {
4618
+ error(e, parent);
4619
+ return;
4620
+ }
4621
+ visitor(e, null);
4622
+ return;
4623
+ case "jsonData":
4624
+ if (parent && parent != "jsonDataResources" && parent !== "resources") {
4625
+ error(e, parent);
4626
+ return;
4627
+ }
4628
+ visitor(e, "JsonData");
4629
+ return;
4630
+ case "build":
4631
+ if (parent && parent !== "resources") {
4632
+ error(e, parent);
4633
+ return;
4634
+ }
4635
+ visitor(e, null);
4636
+ return;
4637
+ }
4638
+ });
4639
+ }
4640
+ function add_resources_to_ast(ast, resources) {
4641
+ Object.entries(resources).forEach(([barrel, resourceMap]) => {
4642
+ const rezModules = {
4643
+ Drawables: [],
4644
+ Fonts: [],
4645
+ JsonData: [],
4646
+ Layouts: [],
4647
+ Menus: [],
4648
+ Properties: [],
4649
+ Strings: [],
4650
+ };
4651
+ Object.values(resourceMap).forEach((rez) => {
4652
+ if (!rez || !(rez instanceof external_sdk_util_cjs_namespaceObject.xmlUtil.Document))
4653
+ return;
4654
+ visit_resources(rez.body.elements, null, (e, s) => {
4655
+ if (!s)
4656
+ return;
4657
+ if (!ast_hasProperty(rezModules, s))
4658
+ return;
4659
+ rezModules[s].push(e);
4660
+ }, (_e, _s) => {
4661
+ return;
4662
+ });
4663
+ });
4664
+ const outerLoc = ast.loc && { ...ast.loc };
4665
+ const makeModule = (m) => ({
4666
+ type: "ModuleDeclaration",
4667
+ id: { type: "Identifier", name: m },
4668
+ body: { type: "BlockStatement", body: [] },
4669
+ loc: outerLoc,
4670
+ });
4671
+ let body = ast.body;
4672
+ if (barrel !== "") {
4673
+ const module = makeModule(barrel);
4674
+ body.push(module);
4675
+ body = module.body.body;
4676
+ }
4677
+ const rez = makeModule("Rez");
4678
+ body.push(rez);
4679
+ body = rez.body.body;
4680
+ Object.entries(rezModules).forEach(([m, elements]) => {
4681
+ const module = makeModule(m);
4682
+ body.push(module);
4683
+ elements.forEach((e) => e.attr.id &&
4684
+ module.body.body.push({
4685
+ type: "VariableDeclaration",
4686
+ declarations: [
4687
+ {
4688
+ type: "VariableDeclarator",
4689
+ kind: "var",
4690
+ id: { type: "Identifier", name: e.attr.id, loc: e.loc },
4691
+ loc: e.loc,
4692
+ },
4693
+ ],
4694
+ kind: "var",
4695
+ loc: e.loc,
4696
+ }));
4697
+ });
4698
+ });
4699
+ }
4700
+
4701
+ ;// CONCATENATED MODULE: ./src/visitor.ts
4702
+
4703
+ function visitorNode(node) {
4704
+ if (node.type === "Identifier") {
4705
+ return node;
4706
+ }
4707
+ if (node.type === "MemberExpression") {
4708
+ return node.property;
4709
+ }
4710
+ if (node.type === "BinaryExpression" &&
4711
+ node.operator === "has" &&
4712
+ node.right.type === "UnaryExpression" &&
4713
+ node.right.operator === ":") {
4714
+ return node.right.argument;
4715
+ }
4716
+ return node;
4717
+ }
4718
+ function visitor_visitReferences(state, ast, name, defn, callback) {
4719
+ const checkResults = ([name, results], node) => {
4720
+ if (name && results) {
4721
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
4722
+ if (callback(node, results, false) === false) {
4723
+ return [];
4724
+ }
4725
+ }
4726
+ }
4727
+ else if (defn === false) {
4728
+ if (callback(node, [], results === null) === false) {
4729
+ return [];
4730
+ }
4731
+ }
4732
+ return null;
4733
+ };
4734
+ state.pre = (node) => {
4735
+ switch (node.type) {
4736
+ case "AttributeList":
4737
+ return [];
4738
+ case "UnaryExpression":
4739
+ // a bare symbol isn't a reference
4740
+ if (node.operator === ":")
4741
+ return [];
4742
+ break;
4743
+ case "BinaryExpression":
4744
+ /*
4745
+ * `expr has :symbol` can be treated as a reference
4746
+ * to expr.symbol.
4747
+ */
4748
+ if (node.operator === "has") {
4749
+ if (node.right.type === "UnaryExpression" &&
4750
+ node.right.operator === ":") {
4751
+ if (!name || node.right.argument.name === name) {
4752
+ return checkResults(state.lookup({
4753
+ type: "MemberExpression",
4754
+ object: node.left,
4755
+ property: node.right.argument,
4756
+ computed: false,
4757
+ }), node);
4758
+ }
4759
+ }
4760
+ }
4761
+ break;
4762
+ case "CallExpression":
4763
+ // A call expression whose callee is an identifier is looked
4764
+ // up as a non-local. ie even if there's a same named local,
4765
+ // it will be ignored, and the lookup will start as if the
4766
+ // call had been written self.foo() rather than foo().
4767
+ if (node.callee.type === "Identifier") {
4768
+ if (!name || node.callee.name === name) {
4769
+ /* ignore return value */
4770
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
4771
+ }
4772
+ return ["arguments"];
4773
+ }
4774
+ break;
4775
+ case "Identifier":
4776
+ if (!name || node.name === name) {
4777
+ return checkResults(state.lookup(node), node);
4778
+ }
4779
+ break;
4780
+ case "MemberExpression": {
4781
+ const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
4782
+ if (property) {
4783
+ if (!name || property.name === name) {
4784
+ return checkResults(state.lookup(node), node) || ["object"];
4785
+ }
4786
+ return ["object"];
4787
+ }
4788
+ break;
4789
+ }
4790
+ case "MethodDefinition": {
4791
+ if (!state.inType) {
4792
+ throw new Error("Method definition outside of type!");
4793
+ }
4794
+ if (node.params) {
4795
+ node.params.forEach((param) => {
4796
+ if (param.type == "BinaryExpression") {
4797
+ state.traverse(param.right);
4798
+ }
4799
+ });
4800
+ }
4801
+ return ["returnType"];
4802
+ }
4803
+ }
4804
+ return null;
4805
+ };
4806
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
4807
+ delete state.pre;
4808
+ }
4809
+
4419
4810
  ;// CONCATENATED MODULE: ./src/api.ts
4420
4811
 
4421
4812
 
@@ -4428,6 +4819,7 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4428
4819
 
4429
4820
 
4430
4821
 
4822
+
4431
4823
  /*
4432
4824
  * This is an unfortunate hack. I want to be able to extract things
4433
4825
  * like the types of all of a Class's variables (in particular the type
@@ -4438,34 +4830,24 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
4438
4830
  * but those are at least in a standard format.
4439
4831
  */
4440
4832
  // Extract all enum values from api.mir
4441
- async function api_getApiMapping(state, barrelList) {
4833
+ async function api_getApiMapping(state, resourcesMap) {
4442
4834
  // get the path to the currently active sdk
4443
4835
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
4444
4836
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
4445
- const rezDecl = `module Rez { ${[
4446
- "Drawables",
4447
- "Fonts",
4448
- "JsonData",
4449
- "Layouts",
4450
- "Menus",
4451
- "Strings",
4452
- ]
4453
- .map((s) => ` module ${s} {}\n`)
4454
- .join("")}}`;
4455
4837
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
4456
4838
  .toString()
4457
4839
  .replace(/\r\n/g, "\n")
4458
4840
  .replace(/^\s*\[.*?\]\s*$/gm, "")
4459
4841
  //.replace(/(COLOR_TRANSPARENT|LAYOUT_[HV]ALIGN_\w+) = (\d+)/gm, "$1 = -$2")
4460
- .replace(/^(\s*type)\s/gm, "$1def ") +
4461
- (barrelList || [])
4462
- .map((name) => `module ${name} { ${rezDecl} }`)
4463
- .concat(rezDecl)
4464
- .join("");
4842
+ .replace(/^(\s*type)\s/gm, "$1def ");
4465
4843
  try {
4466
- const result = api_collectNamespaces(parser.parse(api, null, {
4844
+ const ast = parser.parse(api, null, {
4467
4845
  filepath: "api.mir",
4468
- }), state);
4846
+ });
4847
+ if (resourcesMap) {
4848
+ add_resources_to_ast(ast, resourcesMap);
4849
+ }
4850
+ const result = api_collectNamespaces(ast, state);
4469
4851
  negativeFixups.forEach((fixup) => {
4470
4852
  const vs = fixup.split(".").reduce((state, part) => {
4471
4853
  const decls = api_isStateNode(state) && state.decls?.[part];
@@ -4635,14 +5017,11 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4635
5017
  return current ? current.concat(items) : items;
4636
5018
  }, null);
4637
5019
  if (!result &&
4638
- results.some((ld) => ld.results.some((sn) => (api_isStateNode(sn) && sn.fullName?.match(/^\$\.(\w+\.)?Rez\./)) ||
4639
- sn.type === "VariableDeclarator" ||
5020
+ results.some((ld) => ld.results.some((sn) => sn.type === "VariableDeclarator" ||
4640
5021
  sn.type === "Identifier" ||
4641
5022
  sn.type === "BinaryExpression" ||
4642
5023
  (sn.type === "ClassDeclaration" &&
4643
5024
  property.name === "initialize")))) {
4644
- // - The Rez module can contain lots of things from the resource
4645
- // compiler which we don't track.
4646
5025
  // - Variables, and formal parameters would require type tracking
4647
5026
  // which we don't yet do
4648
5027
  // - Its ok to call an undeclared initialize method.
@@ -4656,7 +5035,7 @@ function lookup(state, decls, node, name, maybeStack, nonlocal) {
4656
5035
  }
4657
5036
  case "ThisExpression": {
4658
5037
  for (let i = stack.length;;) {
4659
- const si = stack[i];
5038
+ const si = stack[--i];
4660
5039
  if (si.type == "ModuleDeclaration" ||
4661
5040
  si.type == "ClassDeclaration" ||
4662
5041
  !i) {