@markw65/monkeyc-optimizer 1.0.29 → 1.0.32

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.
@@ -11163,18 +11163,147 @@ function withLoc(node, start, end) {
11163
11163
  }
11164
11164
  return node;
11165
11165
  }
11166
- function withLocDeep(node, start, end) {
11167
- node = withLoc({ ...node }, start, end);
11166
+ function withLocDeep(node, start, end, inplace) {
11167
+ node = withLoc(inplace ? node : { ...node }, start, end);
11168
11168
  for (const key of mctreeTypeInfo[node.type].keys) {
11169
11169
  const value = node[key];
11170
11170
  if (!value)
11171
11171
  continue;
11172
- const fix = (v) => isMCTreeNode(v) ? withLocDeep(v, start, end) : v;
11172
+ const fix = (v) => isMCTreeNode(v) ? withLocDeep(v, start, end, inplace) : v;
11173
11173
  const repl = Array.isArray(value) ? value.map(fix) : fix(value);
11174
- node[key] = repl;
11174
+ inplace || (node[key] = repl);
11175
11175
  }
11176
11176
  return node;
11177
11177
  }
11178
+ function cloneDeep(node) {
11179
+ return withLocDeep(node, null);
11180
+ }
11181
+
11182
+ ;// CONCATENATED MODULE: ./src/function-info.ts
11183
+
11184
+ function cloneSet(ae) {
11185
+ return new Set(ae);
11186
+ }
11187
+ function mergeSet(a, b) {
11188
+ b.forEach((event) => a.add(event));
11189
+ }
11190
+ function recordModifiedDecl(func, decl) {
11191
+ if (!func.next_info) {
11192
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
11193
+ }
11194
+ func.next_info.modifiedDecls.add(decl);
11195
+ return null;
11196
+ }
11197
+ function recordModifiedDecls(func, lookupDefs) {
11198
+ lookupDefs.forEach((lookupDef) => lookupDef.results.forEach((result) => {
11199
+ if (result.type == "VariableDeclarator" && result.node.kind === "var") {
11200
+ recordModifiedDecl(func, result);
11201
+ }
11202
+ }));
11203
+ }
11204
+ function recordModifiedName(func, name) {
11205
+ if (!func.next_info) {
11206
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
11207
+ }
11208
+ if (!func.next_info.modifiedNames) {
11209
+ func.next_info.modifiedNames = new Set();
11210
+ }
11211
+ func.next_info.modifiedNames.add(name);
11212
+ }
11213
+ function recordModifiedUnknown(func) {
11214
+ if (!func.next_info) {
11215
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
11216
+ }
11217
+ func.next_info.modifiedUnknown = true;
11218
+ }
11219
+ function recordCalledFunc(func, callee) {
11220
+ if (!func.next_info) {
11221
+ func.next_info = { modifiedDecls: new Set(), calledFuncs: new Set() };
11222
+ }
11223
+ func.next_info.calledFuncs.add(callee);
11224
+ return null;
11225
+ }
11226
+ function recordCalledFuncs(func, callees) {
11227
+ callees.forEach((callee) => {
11228
+ recordCalledFunc(func, callee);
11229
+ });
11230
+ }
11231
+ function functionMayModify(state, func, decl) {
11232
+ const info = func.info;
11233
+ if (!info || info.modifiedUnknown)
11234
+ return true;
11235
+ if (info.resolvedDecls) {
11236
+ return info.resolvedDecls.has(decl);
11237
+ }
11238
+ if (info.modifiedNames?.has(decl.name))
11239
+ return true;
11240
+ if (info.modifiedDecls.has(decl))
11241
+ return true;
11242
+ const visited = new Set();
11243
+ const resolved = new Set();
11244
+ const resolveDecls = (f) => {
11245
+ if (visited.has(f))
11246
+ return true;
11247
+ if (!f.info)
11248
+ return false;
11249
+ if (f.info.modifiedUnknown) {
11250
+ info.modifiedUnknown = true;
11251
+ return false;
11252
+ }
11253
+ if (f.info.modifiedNames) {
11254
+ if (info.modifiedNames) {
11255
+ mergeSet(info.modifiedNames, f.info.modifiedNames);
11256
+ }
11257
+ else {
11258
+ info.modifiedNames = cloneSet(f.info.modifiedNames);
11259
+ }
11260
+ }
11261
+ mergeSet(resolved, f.info.modifiedDecls);
11262
+ visited.add(f);
11263
+ const q = true;
11264
+ if (q &&
11265
+ f.info.callsExposed &&
11266
+ state.exposed &&
11267
+ !Object.keys(state.exposed).every((key) => !state.allFunctions[key] ||
11268
+ state.allFunctions[key].every(resolveDecls))) {
11269
+ return false;
11270
+ }
11271
+ return Array.from(f.info.calledFuncs).every(resolveDecls);
11272
+ };
11273
+ if (resolveDecls(func)) {
11274
+ info.resolvedDecls = resolved;
11275
+ return resolved.has(decl);
11276
+ }
11277
+ return true;
11278
+ }
11279
+ function findCallees(lookupDefs) {
11280
+ const decls = lookupDefs.reduce((decls, r) => (decls ? decls.concat(r.results) : r.results), null);
11281
+ return (decls &&
11282
+ decls.filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
11283
+ }
11284
+ function findCalleesForNew(lookupDefs) {
11285
+ const initializer = (decl) => {
11286
+ if (hasProperty(decl.decls, "initialize")) {
11287
+ return decl.decls["initialize"];
11288
+ }
11289
+ if (decl.superClass && decl.superClass !== true) {
11290
+ return decl.superClass.reduce((cur, cls) => {
11291
+ const init = initializer(cls);
11292
+ if (init) {
11293
+ if (!cur)
11294
+ return init;
11295
+ return cur.concat(init);
11296
+ }
11297
+ return cur;
11298
+ }, null);
11299
+ }
11300
+ return null;
11301
+ };
11302
+ return lookupDefs.flatMap((r) => r.results
11303
+ .filter((decl) => decl.type === "ClassDeclaration")
11304
+ .flatMap(initializer)
11305
+ .filter((decl) => decl ? decl.type === "FunctionDeclaration" : false));
11306
+ }
11178
11307
 
11179
11308
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
11180
11309
 
@@ -11236,6 +11365,7 @@ function renameVariable(state, locals, declName) {
11236
11365
 
11237
11366
 
11238
11367
 
11368
+
11239
11369
  function getArgSafety(state, func, args, requireAll) {
11240
11370
  // determine whether decl might be changed by a function call
11241
11371
  // or assignment during the evaluation of FunctionStateNode.
@@ -11268,8 +11398,9 @@ function getArgSafety(state, func, args, requireAll) {
11268
11398
  }
11269
11399
  };
11270
11400
  const safeArgs = [];
11401
+ const argDecls = [];
11271
11402
  let allSafe = true;
11272
- if (!args.every((arg) => {
11403
+ if (!args.every((arg, i) => {
11273
11404
  switch (arg.type) {
11274
11405
  case "Literal":
11275
11406
  safeArgs.push(true);
@@ -11283,13 +11414,17 @@ function getArgSafety(state, func, args, requireAll) {
11283
11414
  safeArgs.push(null);
11284
11415
  return !requireAll;
11285
11416
  }
11286
- const safety = getSafety(results[0].results[0]);
11417
+ const decl = results[0].results[0];
11418
+ const safety = getSafety(decl);
11287
11419
  safeArgs.push(safety);
11288
11420
  if (!safety) {
11289
11421
  allSafe = false;
11290
11422
  if (safety === null) {
11291
11423
  return !requireAll;
11292
11424
  }
11425
+ else if (decl.type === "VariableDeclarator") {
11426
+ argDecls[i] = decl;
11427
+ }
11293
11428
  }
11294
11429
  return true;
11295
11430
  }
@@ -11302,34 +11437,91 @@ function getArgSafety(state, func, args, requireAll) {
11302
11437
  }
11303
11438
  if (allSafe && requireAll)
11304
11439
  return true;
11305
- let callSeen = false;
11440
+ const callsSeen = new Set();
11441
+ const modifiedDecls = new Set();
11442
+ let modifiedUnknown = false;
11306
11443
  const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
11307
11444
  // look for uses of "unsafe" args that occur after a call.
11308
11445
  // use post to do the checking, because arguments are evaluated
11309
11446
  // prior to the call, so eg "return f(x.y);" is fine, but
11310
11447
  // "return f()+x.y" is not.
11311
- traverseAst(func.node.body, null, (node) => {
11312
- switch (node.type) {
11313
- case "AssignmentExpression":
11314
- case "UpdateExpression": {
11315
- const v = node.type == "UpdateExpression" ? node.argument : node.left;
11316
- if (v.type === "Identifier" && (0,external_api_cjs_namespaceObject.hasProperty)(params, v.name)) {
11317
- safeArgs[params[v.name]] = null;
11448
+ const { pre, post, stack } = state;
11449
+ try {
11450
+ delete state.pre;
11451
+ state.post = (node) => {
11452
+ switch (node.type) {
11453
+ case "AssignmentExpression":
11454
+ case "UpdateExpression": {
11455
+ const v = node.type == "UpdateExpression" ? node.argument : node.left;
11456
+ if (v.type === "Identifier" && (0,external_api_cjs_namespaceObject.hasProperty)(params, v.name)) {
11457
+ // If a parameter is modified, we can't just substitute the
11458
+ // argument wherever the parameter is used.
11459
+ safeArgs[params[v.name]] = null;
11460
+ break;
11461
+ }
11462
+ if (modifiedUnknown)
11463
+ break;
11464
+ const [, results] = state.lookup(v);
11465
+ if (results) {
11466
+ results.forEach((r) => r.results.forEach((decl) => decl.type === "VariableDeclarator" && modifiedDecls.add(decl)));
11467
+ }
11468
+ else {
11469
+ modifiedUnknown = true;
11470
+ }
11471
+ break;
11318
11472
  }
11473
+ case "CallExpression":
11474
+ case "NewExpression":
11475
+ if (!modifiedUnknown) {
11476
+ const [, results] = state.lookup(node.callee, null,
11477
+ // calls are looked up as non-locals, but new is not
11478
+ node.type === "CallExpression" ? func.stack : state.stack);
11479
+ if (!results) {
11480
+ const callee_name = node.callee.type === "Identifier"
11481
+ ? node.callee
11482
+ : node.callee.type === "MemberExpression"
11483
+ ? (0,external_api_cjs_namespaceObject.isLookupCandidate)(node.callee)
11484
+ : null;
11485
+ if (callee_name) {
11486
+ const callees = state.allFunctions[callee_name.name];
11487
+ if (callees) {
11488
+ callees.forEach((callee) => callsSeen.add(callee));
11489
+ }
11490
+ }
11491
+ else {
11492
+ modifiedUnknown = true;
11493
+ }
11494
+ }
11495
+ else {
11496
+ const callees = node.type === "CallExpression"
11497
+ ? findCallees(results)
11498
+ : findCalleesForNew(results);
11499
+ if (callees) {
11500
+ callees.forEach((callee) => callsSeen.add(callee));
11501
+ }
11502
+ }
11503
+ }
11504
+ break;
11505
+ case "Identifier":
11506
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name) &&
11507
+ !safeArgs[params[node.name]] &&
11508
+ (modifiedUnknown ||
11509
+ !argDecls[params[node.name]] ||
11510
+ modifiedDecls.has(argDecls[params[node.name]]) ||
11511
+ Array.from(callsSeen).some((callee) => functionMayModify(state, callee, argDecls[params[node.name]])))) {
11512
+ safeArgs[params[node.name]] = null;
11513
+ }
11319
11514
  }
11320
- // fall through
11321
- case "CallExpression":
11322
- case "NewExpression":
11323
- callSeen = true;
11324
- break;
11325
- case "Identifier":
11326
- if (callSeen &&
11327
- (0,external_api_cjs_namespaceObject.hasProperty)(params, node.name) &&
11328
- !safeArgs[params[node.name]]) {
11329
- safeArgs[params[node.name]] = null;
11330
- }
11331
- }
11332
- });
11515
+ return null;
11516
+ };
11517
+ state.stack = func.stack;
11518
+ state.traverse(func.node.body);
11519
+ }
11520
+ finally {
11521
+ state.pre = pre;
11522
+ state.post = post;
11523
+ state.stack = stack;
11524
+ }
11333
11525
  return safeArgs;
11334
11526
  }
11335
11527
  function canInline(state, func, args) {
@@ -11434,9 +11626,6 @@ function processInlineBody(state, func, call, root, params) {
11434
11626
  state.pre = (node) => {
11435
11627
  if (failed)
11436
11628
  return [];
11437
- node.start = call.start;
11438
- node.end = call.end;
11439
- node.loc = call.loc;
11440
11629
  if (replacements.has(node))
11441
11630
  return false;
11442
11631
  const result = pre(node, state);
@@ -11451,6 +11640,7 @@ function processInlineBody(state, func, call, root, params) {
11451
11640
  if (params[paramName] >= 0)
11452
11641
  return null;
11453
11642
  const name = renameVariable(state, locals, paramName) || paramName;
11643
+ locals.map[name] = true;
11454
11644
  return {
11455
11645
  type: "VariableDeclarator",
11456
11646
  id: { type: "Identifier", name },
@@ -11469,31 +11659,49 @@ function processInlineBody(state, func, call, root, params) {
11469
11659
  }
11470
11660
  return result;
11471
11661
  };
11662
+ const fixId = (node) => {
11663
+ if (state.inType)
11664
+ return null;
11665
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
11666
+ const ix = params[node.name];
11667
+ if (ix >= 0) {
11668
+ const replacement = { ...call.arguments[ix] };
11669
+ replacements.add(replacement);
11670
+ return replacement;
11671
+ }
11672
+ return null;
11673
+ }
11674
+ const replacement = fixNodeScope(state, node, func.stack);
11675
+ if (!replacement) {
11676
+ failed = true;
11677
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11678
+ }
11679
+ return replacement;
11680
+ };
11472
11681
  state.post = (node) => {
11473
11682
  if (failed)
11474
11683
  return post(node, state);
11475
11684
  let replacement = null;
11476
11685
  switch (node.type) {
11477
- case "Identifier": {
11478
- if (state.inType)
11479
- break;
11480
- if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
11481
- const ix = params[node.name];
11482
- if (ix >= 0) {
11483
- replacement = { ...call.arguments[ix] };
11484
- replacements.add(replacement);
11485
- return replacement;
11686
+ case "AssignmentExpression":
11687
+ if (node.left.type === "Identifier") {
11688
+ const rep = fixId(node.left);
11689
+ if (rep) {
11690
+ node.left = rep;
11486
11691
  }
11487
- break;
11488
11692
  }
11489
- replacement = fixNodeScope(state, node, func.stack);
11490
- if (!replacement) {
11491
- failed = true;
11492
- inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11493
- return post(node, state);
11693
+ break;
11694
+ case "UpdateExpression":
11695
+ if (node.argument.type === "Identifier") {
11696
+ const rep = fixId(node.argument);
11697
+ if (rep) {
11698
+ node.argument = rep;
11699
+ }
11494
11700
  }
11495
11701
  break;
11496
- }
11702
+ case "Identifier":
11703
+ replacement = fixId(node);
11704
+ break;
11497
11705
  }
11498
11706
  const ret = post(replacement || node, state);
11499
11707
  return ret === false || ret ? ret : replacement;
@@ -11625,6 +11833,10 @@ function inlineWithArgs(state, func, call, context) {
11625
11833
  if (!func.node || !func.node.body) {
11626
11834
  return null;
11627
11835
  }
11836
+ const lastStmt = (block) => {
11837
+ const last = block.body.slice(-1)[0];
11838
+ return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
11839
+ };
11628
11840
  let retStmtCount = 0;
11629
11841
  if (context.type === "ReturnStatement") {
11630
11842
  const last = func.node.body.body.slice(-1)[0];
@@ -11647,7 +11859,7 @@ function inlineWithArgs(state, func, call, context) {
11647
11859
  return null;
11648
11860
  }
11649
11861
  if (retStmtCount === 1) {
11650
- const last = func.node.body.body.slice(-1)[0];
11862
+ const [last] = lastStmt(func.node.body);
11651
11863
  if (!last ||
11652
11864
  last.type !== "ReturnStatement" ||
11653
11865
  ((context.type === "AssignmentExpression" ||
@@ -11658,7 +11870,7 @@ function inlineWithArgs(state, func, call, context) {
11658
11870
  }
11659
11871
  }
11660
11872
  }
11661
- const body = JSON.parse(JSON.stringify(func.node.body));
11873
+ const body = cloneDeep(func.node.body);
11662
11874
  const safeArgs = getArgSafety(state, func, call.arguments, false);
11663
11875
  const params = Object.fromEntries(func.node.params.map((param, i) => {
11664
11876
  const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
@@ -11672,21 +11884,21 @@ function inlineWithArgs(state, func, call, context) {
11672
11884
  }
11673
11885
  diagnostic(state, call.loc, null);
11674
11886
  if (context.type !== "ReturnStatement" && retStmtCount) {
11675
- const last = body.body[body.body.length - 1];
11887
+ const [last, block] = lastStmt(body);
11676
11888
  if (last.type != "ReturnStatement") {
11677
11889
  throw new Error("ReturnStatement got lost!");
11678
11890
  }
11679
11891
  if (last.argument) {
11680
11892
  if (context.type === "AssignmentExpression") {
11681
11893
  context.right = last.argument;
11682
- body.body[body.body.length - 1] = {
11894
+ block.body[block.body.length - 1] = {
11683
11895
  type: "ExpressionStatement",
11684
11896
  expression: context,
11685
11897
  };
11686
11898
  }
11687
11899
  else if (context.type === "VariableDeclarator") {
11688
11900
  const { id, init: _init, kind: _kind, ...rest } = context;
11689
- body.body[body.body.length - 1] = {
11901
+ block.body[block.body.length - 1] = {
11690
11902
  ...rest,
11691
11903
  type: "ExpressionStatement",
11692
11904
  expression: {
@@ -11700,25 +11912,26 @@ function inlineWithArgs(state, func, call, context) {
11700
11912
  }
11701
11913
  else {
11702
11914
  const side_exprs = unused(last.argument);
11703
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
11915
+ block.body.splice(block.body.length - 1, 1, ...side_exprs);
11704
11916
  }
11705
11917
  }
11706
11918
  else {
11707
- --body.body.length;
11919
+ --block.body.length;
11708
11920
  }
11709
11921
  }
11922
+ withLocDeep(body, context, context, true);
11710
11923
  return body;
11711
11924
  }
11712
11925
  function inlineFunction(state, func, call, context) {
11713
11926
  if (context) {
11714
11927
  return inlineWithArgs(state, func, call, context);
11715
11928
  }
11716
- const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
11929
+ const retArg = cloneDeep(func.node.body.body[0].argument);
11717
11930
  const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
11718
11931
  const map = fixupLocalsMap(state);
11719
11932
  const ret = processInlineBody(state, func, call, retArg, params);
11720
11933
  state.localsStack[state.localsStack.length - 1].map = map;
11721
- return ret;
11934
+ return ret && withLocDeep(ret, call, call, true);
11722
11935
  }
11723
11936
  function applyTypeIfNeeded(node) {
11724
11937
  if ("enumType" in node && node.enumType) {
@@ -11823,6 +12036,121 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11823
12036
  return null;
11824
12037
  }
11825
12038
 
12039
+ ;// CONCATENATED MODULE: ./src/pragma-checker.ts
12040
+
12041
+
12042
+
12043
+ function pragmaChecker(state, ast, diagnostics) {
12044
+ const comments = ast.comments;
12045
+ if (!comments)
12046
+ return;
12047
+ diagnostics = diagnostics
12048
+ ?.slice()
12049
+ .sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
12050
+ let diagIndex = 0;
12051
+ let index = -1;
12052
+ let comment;
12053
+ let matchers;
12054
+ const next = () => {
12055
+ while (++index < comments.length) {
12056
+ comment = comments[index];
12057
+ let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
12058
+ if (!match)
12059
+ continue;
12060
+ const kind = match[1];
12061
+ let str = match[2];
12062
+ matchers = [];
12063
+ while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
12064
+ matchers.push({ kind, quote: match[1], needle: match[2] });
12065
+ str = str.substring(match[0].length);
12066
+ if (!str.length)
12067
+ break;
12068
+ }
12069
+ if (!str.length)
12070
+ break;
12071
+ if (!matchers.length) {
12072
+ match = str.match(/^(\S+)\s+$/);
12073
+ if (match) {
12074
+ matchers.push({ kind, quote: '"', needle: match[1] });
12075
+ break;
12076
+ }
12077
+ }
12078
+ diagnostic(state, comment.loc, `Build pragma '${comment.value}' is invalid`, "ERROR");
12079
+ }
12080
+ };
12081
+ const matcher = (quote, needle, haystack) => {
12082
+ if (quote == '"') {
12083
+ return haystack.includes(needle);
12084
+ }
12085
+ const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
12086
+ return re.test(haystack);
12087
+ };
12088
+ next();
12089
+ traverseAst(ast, (node) => {
12090
+ if (index >= comments.length)
12091
+ return false;
12092
+ if (node.start && node.start >= (comment.end || Infinity)) {
12093
+ const { kind, quote, needle } = matchers.shift();
12094
+ if (kind === "match") {
12095
+ const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
12096
+ if (!matcher(quote, needle, haystack)) {
12097
+ matcher(quote, needle, haystack);
12098
+ diagnostic(state, comment.loc, `Didn't find '${needle}' in '${haystack}'`, "ERROR");
12099
+ }
12100
+ }
12101
+ else if (kind === "expect") {
12102
+ const locCmp = (a, b) => {
12103
+ if (!b)
12104
+ return -1;
12105
+ if (a.start.line < b.start.line)
12106
+ return -1;
12107
+ if (a.start.line === b.start.line &&
12108
+ a.start.column < b.start.column) {
12109
+ return -1;
12110
+ }
12111
+ if (a.end.line > b.end.line)
12112
+ return 1;
12113
+ if (a.end.line === b.end.line && a.end.column >= b.end.column) {
12114
+ return 1;
12115
+ }
12116
+ return 0;
12117
+ };
12118
+ let found = false;
12119
+ if (diagnostics) {
12120
+ while (true) {
12121
+ if (diagIndex >= diagnostics.length) {
12122
+ diagnostics = null;
12123
+ break;
12124
+ }
12125
+ const diag = diagnostics[diagIndex];
12126
+ const cmp = locCmp(diag.loc, node.loc);
12127
+ if (cmp > 0) {
12128
+ break;
12129
+ }
12130
+ diagIndex++;
12131
+ if (cmp < 0)
12132
+ continue;
12133
+ if (matcher(quote, needle, diag.message)) {
12134
+ found = true;
12135
+ diag.type = "INFO";
12136
+ }
12137
+ }
12138
+ }
12139
+ if (!found) {
12140
+ diagnostic(state, comment.loc, `Missing error message '${needle}`, "ERROR");
12141
+ }
12142
+ }
12143
+ if (matchers.length) {
12144
+ // if we're checking a series of nodes, we need
12145
+ // to skip over this one.
12146
+ return false;
12147
+ }
12148
+ next();
12149
+ }
12150
+ return null;
12151
+ });
12152
+ }
12153
+
11826
12154
  ;// CONCATENATED MODULE: ./src/control-flow.ts
11827
12155
 
11828
12156
 
@@ -11966,7 +12294,8 @@ function buildReducedGraph(state, func, notice) {
11966
12294
  if (node.type === "WhileStatement") {
11967
12295
  head = localState.newBlock(top.continue);
11968
12296
  state.traverse(node.test);
11969
- localState.addEdge(localState.newBlock(), top.break);
12297
+ localState.addEdge(localState.curBlock, top.break);
12298
+ localState.newBlock();
11970
12299
  }
11971
12300
  else {
11972
12301
  head = localState.newBlock();
@@ -12287,6 +12616,8 @@ function cleanCfg(head) {
12287
12616
  }
12288
12617
  }));
12289
12618
  }
12619
+ if (!cur.node)
12620
+ cur.node = succ.node;
12290
12621
  }
12291
12622
  }
12292
12623
  });
@@ -12340,6 +12671,8 @@ var priorityqueuejs = __webpack_require__(2789);
12340
12671
 
12341
12672
 
12342
12673
 
12674
+
12675
+
12343
12676
  /**
12344
12677
  * This implements a pseudo Partial Redundancy Elimination
12345
12678
  * pass. It isn't quite like traditional PRE because we're
@@ -12385,6 +12718,20 @@ function declName(decl) {
12385
12718
  throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12386
12719
  }
12387
12720
  }
12721
+ function logAntState(s, decl) {
12722
+ const defs = Array.from(s.ant).reduce((defs, event) => {
12723
+ if (event.type === "def" || event.type === "mod")
12724
+ defs++;
12725
+ return defs;
12726
+ }, 0);
12727
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
12728
+ console.log(` - members: ${Array.from(s.members)
12729
+ .map(([block, live]) => block.order + (live ? "t" : "f"))
12730
+ .join(", ")}`);
12731
+ }
12732
+ function logAntDecls(antDecls) {
12733
+ antDecls.forEach(logAntState);
12734
+ }
12388
12735
  function sizeBasedPRE(state, func) {
12389
12736
  if (!func.node.body)
12390
12737
  return;
@@ -12395,18 +12742,11 @@ function sizeBasedPRE(state, func) {
12395
12742
  return;
12396
12743
  }
12397
12744
  const { graph: head, identifiers } = buildPREGraph(state, func);
12398
- const candidates = computeAttributes(head);
12745
+ const candidates = computeAttributes(state, head);
12399
12746
  if (candidates) {
12400
12747
  if (logging) {
12401
12748
  console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
12402
- candidates.forEach((s, decl) => {
12403
- const defs = Array.from(s.ant).reduce((defs, event) => {
12404
- if (event.type === "def")
12405
- defs++;
12406
- return defs;
12407
- }, 0);
12408
- console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
12409
- });
12749
+ logAntDecls(candidates);
12410
12750
  }
12411
12751
  const nodeMap = new Map();
12412
12752
  const declMap = new Map();
@@ -12422,8 +12762,10 @@ function sizeBasedPRE(state, func) {
12422
12762
  let i = 0;
12423
12763
  do {
12424
12764
  name = `pre_${declName(decl)}${i ? "_" + i : ""}`;
12425
- if (!identifiers.has(name))
12765
+ if (!identifiers.has(name)) {
12766
+ identifiers.add(name);
12426
12767
  break;
12768
+ }
12427
12769
  i++;
12428
12770
  } while (true);
12429
12771
  declMap.set(decl, name);
@@ -12514,11 +12856,14 @@ function buildPREGraph(state, func) {
12514
12856
  case "ParenthesizedExpression":
12515
12857
  break;
12516
12858
  case "Literal":
12517
- if (!node.value && refCost(node) > LocalRefCost) {
12518
- let decl = literals.get(node.value);
12859
+ if (refCost(node) > LocalRefCost) {
12860
+ const key = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.test(node.raw)
12861
+ ? BigInt(node.value)
12862
+ : node.value;
12863
+ let decl = literals.get(key);
12519
12864
  if (!decl) {
12520
12865
  decl = node;
12521
- literals.set(node.value, decl);
12866
+ literals.set(key, decl);
12522
12867
  }
12523
12868
  return {
12524
12869
  type: "ref",
@@ -12598,10 +12943,18 @@ function buildPREGraph(state, func) {
12598
12943
  }
12599
12944
  break;
12600
12945
  }
12601
- case "NewExpression":
12602
- case "CallExpression":
12946
+ case "NewExpression": {
12947
+ const [, results] = state.lookup(node.callee);
12948
+ const callees = results ? findCalleesForNew(results) : null;
12603
12949
  liveDef(null, stmt);
12604
- return { type: "mod", node, mayThrow };
12950
+ return { type: "mod", node, mayThrow, callees };
12951
+ }
12952
+ case "CallExpression": {
12953
+ liveDef(null, stmt);
12954
+ const [, results] = state.lookup(node.callee);
12955
+ const callees = results ? findCallees(results) : null;
12956
+ return { type: "mod", node, mayThrow, callees };
12957
+ }
12605
12958
  default:
12606
12959
  if (!isExpression(node))
12607
12960
  break;
@@ -12617,12 +12970,6 @@ function buildPREGraph(state, func) {
12617
12970
  function anticipatedDecls() {
12618
12971
  return new Map();
12619
12972
  }
12620
- function cloneSet(ae) {
12621
- return new Set(ae);
12622
- }
12623
- function mergeSet(a, b) {
12624
- b.forEach((event) => a.add(event));
12625
- }
12626
12973
  function equalSet(a, b) {
12627
12974
  if (a.size != b.size)
12628
12975
  return false;
@@ -12652,21 +12999,28 @@ function cloneAnticipatedState(as) {
12652
12999
  members: new Map(as.members),
12653
13000
  };
12654
13001
  }
13002
+ function mergeAnticipatedState(ae, be) {
13003
+ mergeSet(ae.ant, be.ant);
13004
+ be.members.forEach((live, block) => ae.members.set(block, live));
13005
+ if (be.live)
13006
+ ae.live = true;
13007
+ }
12655
13008
  function cloneAnticipatedDecls(ad) {
12656
13009
  const copy = anticipatedDecls();
12657
13010
  for (const [k, v] of ad) {
12658
- copy.set(k, cloneAnticipatedState(v));
13011
+ if (!v.isIsolated) {
13012
+ copy.set(k, cloneAnticipatedState(v));
13013
+ }
12659
13014
  }
12660
13015
  return copy;
12661
13016
  }
12662
13017
  function mergeAnticipatedDecls(a, b) {
12663
13018
  for (const [k, v] of b) {
13019
+ if (v.isIsolated)
13020
+ continue;
12664
13021
  const ae = a.get(k);
12665
13022
  if (ae) {
12666
- mergeSet(ae.ant, v.ant);
12667
- v.members.forEach((live, block) => ae.members.set(block, live));
12668
- if (v.live)
12669
- ae.live = true;
13023
+ mergeAnticipatedState(ae, v);
12670
13024
  }
12671
13025
  else {
12672
13026
  a.set(k, cloneAnticipatedState(v));
@@ -12680,6 +13034,7 @@ function equalStates(a, b) {
12680
13034
  const be = b.get(k);
12681
13035
  if (!be ||
12682
13036
  be.live != ae.live ||
13037
+ be.isIsolated != ae.isIsolated ||
12683
13038
  !equalSet(ae.ant, be.ant) ||
12684
13039
  !equalMap(ae.members, be.members)) {
12685
13040
  return false;
@@ -12693,6 +13048,7 @@ function refCost(node) {
12693
13048
  switch (typeof node.value) {
12694
13049
  case "string":
12695
13050
  return 5;
13051
+ case "bigint":
12696
13052
  case "number":
12697
13053
  return 5;
12698
13054
  case "boolean":
@@ -12750,10 +13106,11 @@ function candidateCost(candState) {
12750
13106
  cost += defCost(candState.node);
12751
13107
  }
12752
13108
  });
12753
- cost += defCost(candState.node) * candidateBoundary(candState).size;
13109
+ const boundarySize = candidateBoundary(candState).size;
13110
+ cost += defCost(candState.node) * boundarySize;
12754
13111
  return cost;
12755
13112
  }
12756
- function computeAttributes(head) {
13113
+ function computeAttributes(state, head) {
12757
13114
  const order = getPostOrder(head);
12758
13115
  order.forEach((block, i) => {
12759
13116
  block.order = i;
@@ -12862,7 +13219,9 @@ function computeAttributes(head) {
12862
13219
  curState.forEach((candidates, decl) => {
12863
13220
  if (decl.type === "VariableDeclarator" &&
12864
13221
  decl.node.kind === "var" &&
12865
- candidates.live) {
13222
+ candidates.live &&
13223
+ (!event.callees ||
13224
+ event.callees.some((callee) => functionMayModify(state, callee, decl)))) {
12866
13225
  candidates.ant.add(getMod(event, decl, candidates.node));
12867
13226
  candidates.live = false;
12868
13227
  }
@@ -12888,9 +13247,7 @@ function computeAttributes(head) {
12888
13247
  if (isUpdate || candidates.live) {
12889
13248
  candidates.ant.add(event);
12890
13249
  }
12891
- if (!isUpdate) {
12892
- candidates.live = false;
12893
- }
13250
+ candidates.live = isUpdate;
12894
13251
  break;
12895
13252
  }
12896
13253
  }
@@ -12899,12 +13256,23 @@ function computeAttributes(head) {
12899
13256
  curState.forEach((antState) => {
12900
13257
  antState.head = top;
12901
13258
  antState.members.set(top, antState.live);
13259
+ if (!antState.live && candidateBoundary(antState).size === 0) {
13260
+ // we found a group that's isolated from the rest
13261
+ // of the function. Don't merge it with earlier
13262
+ // refs and defs, because we can take it or leave
13263
+ // it based on its own cost.
13264
+ antState.isIsolated = true;
13265
+ }
12902
13266
  });
12903
13267
  const oldState = blockStates[top.order];
12904
13268
  if (oldState && equalStates(oldState, curState)) {
12905
13269
  continue;
12906
13270
  }
12907
13271
  blockStates[top.order] = curState;
13272
+ if (logging) {
13273
+ console.log(`Updated block ${top.order}`);
13274
+ logAntDecls(curState);
13275
+ }
12908
13276
  if (top.preds) {
12909
13277
  top.preds.forEach((pred) => enqueue(pred));
12910
13278
  }
@@ -12917,7 +13285,9 @@ function computeAttributes(head) {
12917
13285
  if (cost >= 0)
12918
13286
  return;
12919
13287
  const existing = candidateDecls.get(decl);
12920
- if (!existing || candidateCost(existing) > cost) {
13288
+ if (!existing ||
13289
+ existing.isIsolated ||
13290
+ candidateCost(existing) > cost) {
12921
13291
  const boundary = candidateBoundary(events);
12922
13292
  if (!Array.from(boundary).every((block) => {
12923
13293
  if (block !== events.head && block.events) {
@@ -12957,7 +13327,11 @@ function computeAttributes(head) {
12957
13327
  return;
12958
13328
  }
12959
13329
  events.live = false;
12960
- if (candidateCost(events) != cost) {
13330
+ if (existing && existing.isIsolated) {
13331
+ delete existing.isIsolated;
13332
+ mergeAnticipatedState(events, existing);
13333
+ }
13334
+ else if (candidateCost(events) != cost) {
12961
13335
  throw new Error(`cost of block ${i} changed`);
12962
13336
  }
12963
13337
  candidateDecls.set(decl, events);
@@ -13036,28 +13410,34 @@ function applyReplacements(func, nodeMap, declMap) {
13036
13410
  stmtStack.pop();
13037
13411
  const events = nodeMap.get(node);
13038
13412
  if (events) {
13039
- if (events.length === 1) {
13040
- if (events[0].type === "ref") {
13413
+ const ret = events.reduce((ret, event) => {
13414
+ if (event.type === "ref") {
13415
+ if (ret) {
13416
+ throw new Error(`ref found when there was already a replacement for this node`);
13417
+ }
13041
13418
  if (node.type !== "Identifier" &&
13042
13419
  node.type !== "MemberExpression" &&
13043
13420
  node.type !== "Literal") {
13044
13421
  throw new Error(`Ref found, but wrong type of node: ${node.type}`);
13045
13422
  }
13046
- const name = declMap.get(events[0].decl);
13423
+ const name = declMap.get(event.decl);
13047
13424
  if (!name) {
13048
13425
  throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(node)}"`);
13049
13426
  }
13050
13427
  return ident(name, node);
13051
13428
  }
13052
- else if (events[0].type === "def") {
13053
- if (node.type !== "AssignmentExpression" &&
13429
+ if (event.type === "def") {
13430
+ if (ret) {
13431
+ throw new Error(`def found when there was already a replacement for this node`);
13432
+ }
13433
+ if (node.type !== "AssignmentExpression" &&
13054
13434
  node.type !== "UpdateExpression") {
13055
13435
  throw new Error(`Def found, but wrong type of node: ${node.type}`);
13056
13436
  }
13057
13437
  const target = node.type === "AssignmentExpression"
13058
13438
  ? node.left
13059
13439
  : node.argument;
13060
- const name = declMap.get(events[0].decl);
13440
+ const name = declMap.get(event.decl);
13061
13441
  if (!name) {
13062
13442
  throw new Error(`No replacement found for "${(0,external_api_cjs_namespaceObject.formatAst)(target)}"`);
13063
13443
  }
@@ -13076,20 +13456,24 @@ function applyReplacements(func, nodeMap, declMap) {
13076
13456
  }
13077
13457
  return withLoc({ type: "SequenceExpression", expressions: [node, assign] }, node);
13078
13458
  }
13079
- }
13080
- events.forEach((event) => {
13081
- if (event.type !== "mod") {
13082
- throw new Error(`Unexpected ${event.type} found amongst multiple events`);
13083
- }
13084
- if (!event.decl) {
13085
- throw new Error(`Unexpected null decl on mod event`);
13459
+ if (event.type === "mod") {
13460
+ if (!event.decl) {
13461
+ throw new Error(`Unexpected null decl on mod event`);
13462
+ }
13463
+ let pending = pendingMap.get(stmt);
13464
+ if (!pending) {
13465
+ pendingMap.set(stmt, (pending = new Set()));
13466
+ }
13467
+ pending.add(event);
13086
13468
  }
13087
- let pending = pendingMap.get(stmt);
13088
- if (!pending) {
13089
- pendingMap.set(stmt, (pending = new Set()));
13469
+ else {
13470
+ throw new Error(`Unexpected ${event.type} found`);
13090
13471
  }
13091
- pending.add(event);
13092
- });
13472
+ return ret;
13473
+ }, null);
13474
+ if (ret) {
13475
+ return ret;
13476
+ }
13093
13477
  }
13094
13478
  const pending = pendingMap.get(node);
13095
13479
  if (node.type === "SequenceExpression") {
@@ -13163,6 +13547,148 @@ function applyReplacements(func, nodeMap, declMap) {
13163
13547
  });
13164
13548
  }
13165
13549
 
13550
+ ;// CONCATENATED MODULE: ./src/unused-exprs.ts
13551
+
13552
+
13553
+
13554
+ function cleanupUnusedVars(state, node) {
13555
+ const [parent] = state.stack.slice(-1);
13556
+ if (parent.node !== node) {
13557
+ return;
13558
+ }
13559
+ if (parent.type != "BlockStatement") {
13560
+ throw new Error(`Unexpected parent type '${parent.type}' for local declaration`);
13561
+ }
13562
+ if (parent.decls) {
13563
+ let toRemove = null;
13564
+ Object.values(parent.decls).forEach((decls) => {
13565
+ if (decls.length === 1 &&
13566
+ decls[0].type === "VariableDeclarator" &&
13567
+ !decls[0].used) {
13568
+ if (!toRemove)
13569
+ toRemove = {};
13570
+ toRemove[decls[0].name] = decls[0];
13571
+ }
13572
+ });
13573
+ if (toRemove) {
13574
+ const varDeclarations = new Map();
13575
+ traverseAst(node, null, (node) => {
13576
+ switch (node.type) {
13577
+ case "VariableDeclaration": {
13578
+ node.declarations.forEach((decl, i) => {
13579
+ const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(decl.id);
13580
+ if (hasProperty(toRemove, name)) {
13581
+ const indices = varDeclarations.get(node);
13582
+ if (indices) {
13583
+ indices.push(i);
13584
+ }
13585
+ else {
13586
+ varDeclarations.set(node, [i]);
13587
+ }
13588
+ }
13589
+ });
13590
+ break;
13591
+ }
13592
+ case "ExpressionStatement":
13593
+ if (node.expression.type === "AssignmentExpression") {
13594
+ if (node.expression.left.type === "Identifier" &&
13595
+ hasProperty(toRemove, node.expression.left.name)) {
13596
+ return unused(node.expression.right);
13597
+ }
13598
+ }
13599
+ else if (node.expression.type === "UpdateExpression" &&
13600
+ node.expression.argument.type === "Identifier" &&
13601
+ hasProperty(toRemove, node.expression.argument.name)) {
13602
+ return false;
13603
+ }
13604
+ break;
13605
+ case "SequenceExpression": {
13606
+ for (let i = node.expressions.length; i--;) {
13607
+ const expr = node.expressions[i];
13608
+ if (expr.type === "AssignmentExpression") {
13609
+ if (expr.left.type === "Identifier" &&
13610
+ hasProperty(toRemove, expr.left.name)) {
13611
+ const rep = unused(expr.right);
13612
+ if (!rep.length) {
13613
+ node.expressions.splice(i, 1);
13614
+ }
13615
+ else {
13616
+ // Sequence expressions can only be assignments
13617
+ // or update expressions. Even calls aren't allowed
13618
+ toRemove[expr.left.name] = null;
13619
+ expr.operator = "=";
13620
+ }
13621
+ }
13622
+ }
13623
+ else if (expr.type === "UpdateExpression" &&
13624
+ expr.argument.type === "Identifier" &&
13625
+ hasProperty(toRemove, expr.argument.name)) {
13626
+ node.expressions.splice(i, 1);
13627
+ }
13628
+ }
13629
+ break;
13630
+ }
13631
+ }
13632
+ return null;
13633
+ });
13634
+ varDeclarations.forEach((indices, decl) => {
13635
+ let index = -1;
13636
+ for (let ii = indices.length, j = decl.declarations.length; ii--;) {
13637
+ const i = indices[ii];
13638
+ const vdecl = decl.declarations[i];
13639
+ const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(vdecl.id);
13640
+ if (hasProperty(toRemove, name)) {
13641
+ const rep = vdecl.init ? unused(vdecl.init) : [];
13642
+ if (rep.length) {
13643
+ if (parent.node.type === "ForStatement") {
13644
+ // declarations whose inits have side effects
13645
+ // can't be deleted from for statements.
13646
+ continue;
13647
+ }
13648
+ if (index < 0) {
13649
+ index = parent.node.body.findIndex((s) => s === decl);
13650
+ if (index < 0) {
13651
+ throw new Error(`Failed to find variable declaration for ${(0,external_api_cjs_namespaceObject.variableDeclarationName)(vdecl.id)}`);
13652
+ }
13653
+ }
13654
+ if (j > i + 1) {
13655
+ const tail = {
13656
+ ...decl,
13657
+ declarations: decl.declarations.slice(i + 1, j),
13658
+ };
13659
+ if (decl.loc && vdecl.loc) {
13660
+ tail.loc = { ...decl.loc, start: vdecl.loc.end };
13661
+ tail.start = vdecl.end;
13662
+ }
13663
+ rep.push(tail);
13664
+ }
13665
+ if (decl.loc && vdecl.loc) {
13666
+ decl.loc = { ...decl.loc, end: vdecl.loc.start };
13667
+ decl.end = vdecl.start;
13668
+ }
13669
+ decl.declarations.splice(i);
13670
+ parent.node.body.splice(index + 1, 0, ...rep);
13671
+ j = i;
13672
+ continue;
13673
+ }
13674
+ if (toRemove[name]) {
13675
+ j--;
13676
+ decl.declarations.splice(i, 1);
13677
+ if (i === j && decl.loc && vdecl.loc) {
13678
+ decl.loc = { ...decl.loc, end: vdecl.loc.start };
13679
+ decl.end = vdecl.start;
13680
+ }
13681
+ }
13682
+ else {
13683
+ delete vdecl.init;
13684
+ }
13685
+ }
13686
+ }
13687
+ });
13688
+ }
13689
+ }
13690
+ }
13691
+
13166
13692
  ;// CONCATENATED MODULE: ./src/visitor.ts
13167
13693
 
13168
13694
  function visitReferences(state, ast, name, defn, callback) {
@@ -13227,14 +13753,16 @@ function visitReferences(state, ast, name, defn, callback) {
13227
13753
  return checkResults(state.lookup(node), node);
13228
13754
  }
13229
13755
  break;
13230
- case "MemberExpression":
13231
- if (!node.computed && node.property.type === "Identifier") {
13232
- if (!name || node.property.name === name) {
13756
+ case "MemberExpression": {
13757
+ const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
13758
+ if (property) {
13759
+ if (!name || property.name === name) {
13233
13760
  return checkResults(state.lookup(node), node) || ["object"];
13234
13761
  }
13235
13762
  return ["object"];
13236
13763
  }
13237
13764
  break;
13765
+ }
13238
13766
  case "MethodDefinition": {
13239
13767
  if (!state.inType) {
13240
13768
  throw new Error("Method definition outside of type!");
@@ -13243,7 +13771,6 @@ function visitReferences(state, ast, name, defn, callback) {
13243
13771
  node.params.forEach((param) => {
13244
13772
  if (param.type == "BinaryExpression") {
13245
13773
  state.traverse(param.right);
13246
- state.inType = true;
13247
13774
  }
13248
13775
  });
13249
13776
  }
@@ -13266,6 +13793,9 @@ function visitReferences(state, ast, name, defn, callback) {
13266
13793
 
13267
13794
 
13268
13795
 
13796
+
13797
+
13798
+
13269
13799
  function collectClassInfo(state) {
13270
13800
  const toybox = state.stack[0].decls["Toybox"][0];
13271
13801
  const lang = toybox.decls["Lang"][0];
@@ -13325,8 +13855,7 @@ function collectClassInfo(state) {
13325
13855
  c.decls &&
13326
13856
  Object.values(c.decls).forEach((funcs) => {
13327
13857
  funcs.forEach((f) => {
13328
- if ((0,external_api_cjs_namespaceObject.isStateNode)(f) &&
13329
- f.type === "FunctionDeclaration" &&
13858
+ if (f.type === "FunctionDeclaration" &&
13330
13859
  (0,external_api_cjs_namespaceObject.hasProperty)(cls.decls, f.name)) {
13331
13860
  f.node.hasOverride = true;
13332
13861
  }
@@ -13339,6 +13868,15 @@ function collectClassInfo(state) {
13339
13868
  state.allClasses.forEach((elm) => {
13340
13869
  if (elm.superClass)
13341
13870
  markOverrides(elm, elm.superClass);
13871
+ if (elm.hasInvoke && elm.decls) {
13872
+ Object.values(elm.decls).forEach((funcs) => {
13873
+ funcs.forEach((f) => {
13874
+ if (f.type === "FunctionDeclaration" && !f.isStatic) {
13875
+ (0,external_api_cjs_namespaceObject.markInvokeClassMethod)(f);
13876
+ }
13877
+ });
13878
+ });
13879
+ }
13342
13880
  });
13343
13881
  }
13344
13882
  function getFileSources(fnMap) {
@@ -13377,7 +13915,7 @@ async function analyze(fnMap, barrelList, config) {
13377
13915
  const preState = {
13378
13916
  fnMap,
13379
13917
  config,
13380
- allFunctions: [],
13918
+ allFunctions: {},
13381
13919
  allClasses: [],
13382
13920
  shouldExclude(node) {
13383
13921
  if ("attrs" in node &&
@@ -13407,11 +13945,6 @@ async function analyze(fnMap, barrelList, config) {
13407
13945
  pre(node, state) {
13408
13946
  switch (node.type) {
13409
13947
  case "FunctionDeclaration":
13410
- if (markApi) {
13411
- node.body = null;
13412
- break;
13413
- }
13414
- // falls through
13415
13948
  case "ModuleDeclaration":
13416
13949
  case "ClassDeclaration": {
13417
13950
  const [scope] = state.stack.slice(-1);
@@ -13422,7 +13955,18 @@ async function analyze(fnMap, barrelList, config) {
13422
13955
  (scope.node.attrs &&
13423
13956
  scope.node.attrs.access &&
13424
13957
  scope.node.attrs.access.includes("static"));
13425
- state.allFunctions.push(scope);
13958
+ if (markApi) {
13959
+ node.body = null;
13960
+ scope.info = (0,external_api_cjs_namespaceObject.getApiFunctionInfo)(scope);
13961
+ delete scope.stack;
13962
+ }
13963
+ const allFuncs = state.allFunctions;
13964
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(allFuncs, scope.name)) {
13965
+ allFuncs[scope.name] = [scope];
13966
+ }
13967
+ else {
13968
+ allFuncs[scope.name].push(scope);
13969
+ }
13426
13970
  }
13427
13971
  else if (scope.type === "ClassDeclaration") {
13428
13972
  state.allClasses.push(scope);
@@ -13447,7 +13991,7 @@ async function analyze(fnMap, barrelList, config) {
13447
13991
  value.hasTests = hasTests;
13448
13992
  });
13449
13993
  delete state.shouldExclude;
13450
- delete state.post;
13994
+ delete state.pre;
13451
13995
  collectClassInfo(state);
13452
13996
  const diagnosticType = config?.checkInvalidSymbols !== "OFF"
13453
13997
  ? config?.checkInvalidSymbols || "WARNING"
@@ -13470,6 +14014,8 @@ async function analyze(fnMap, barrelList, config) {
13470
14014
  });
13471
14015
  });
13472
14016
  }
14017
+ state.exposed = state.nextExposed;
14018
+ state.nextExposed = {};
13473
14019
  return state;
13474
14020
  }
13475
14021
  function compareLiteralLike(a, b) {
@@ -13515,15 +14061,12 @@ function getLiteralNode(node) {
13515
14061
  if (node.argument.type != "Literal")
13516
14062
  return null;
13517
14063
  switch (node.operator) {
13518
- case "-":
13519
- if (typeof node.argument.value == "number") {
13520
- return {
13521
- ...node.argument,
13522
- value: -node.argument.value,
13523
- raw: "-" + node.argument.value,
13524
- enumType: node.enumType,
13525
- };
14064
+ case "-": {
14065
+ const [arg, type] = getNodeValue(node.argument);
14066
+ if (type === "Number" || type === "Long") {
14067
+ return replacementLiteral(arg, -arg.value, type);
13526
14068
  }
14069
+ }
13527
14070
  }
13528
14071
  }
13529
14072
  return null;
@@ -13542,29 +14085,29 @@ function getNodeValue(node) {
13542
14085
  if (node.type != "Literal") {
13543
14086
  return [null, null];
13544
14087
  }
13545
- let type = node.value === null ? "Null" : typeof node.value;
14088
+ if (node.value === null) {
14089
+ return [node, "Null"];
14090
+ }
14091
+ const type = typeof node.value;
13546
14092
  if (type === "number") {
13547
- const match = node.raw && prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
14093
+ const match = prettier_plugin_monkeyc_namespaceObject.LiteralIntegerRe.exec(node.raw);
13548
14094
  if (match) {
13549
- type = match[2] == "l" ? "Long" : "Number";
13550
- }
13551
- else if (node.raw && node.raw.endsWith("d")) {
13552
- type = "Double";
13553
- }
13554
- else {
13555
- type = "Float";
14095
+ return match[2] === "l" || match[2] === "L"
14096
+ ? [node, "Long"]
14097
+ : [node, "Number"];
13556
14098
  }
14099
+ return [node, node.raw.endsWith("d") ? "Double" : "Float"];
13557
14100
  }
13558
- else if (type === "string") {
13559
- type = "String";
14101
+ if (type === "bigint") {
14102
+ return [node, "Long"];
13560
14103
  }
13561
- else if (type === "boolean") {
13562
- type = "Boolean";
14104
+ if (type === "string") {
14105
+ return [node, "String"];
13563
14106
  }
13564
- else {
13565
- type = "Unknown";
14107
+ if (type === "boolean") {
14108
+ return [node, "Boolean"];
13566
14109
  }
13567
- return [node, type];
14110
+ throw new Error(`Literal has unknown type '${type}'`);
13568
14111
  }
13569
14112
  function fullTypeName(state, tsp) {
13570
14113
  if (typeof tsp.name === "string") {
@@ -13609,6 +14152,46 @@ function isBooleanExpression(state, node) {
13609
14152
  }
13610
14153
  return false;
13611
14154
  }
14155
+ function replacementLiteral(arg, value, type) {
14156
+ if (typeof value === "boolean") {
14157
+ type = "Boolean";
14158
+ }
14159
+ else if (type === "Number") {
14160
+ value = Number(BigInt.asIntN(32, BigInt(value)));
14161
+ }
14162
+ else if (type === "Long") {
14163
+ value = BigInt.asIntN(64, BigInt(value));
14164
+ }
14165
+ return {
14166
+ ...arg,
14167
+ value,
14168
+ raw: value.toString() + (type === "Long" ? "l" : ""),
14169
+ };
14170
+ }
14171
+ const operators = {
14172
+ "+": (left, right) => left + right,
14173
+ "-": (left, right) => left - right,
14174
+ "*": (left, right) => left * right,
14175
+ "/": (left, right) => left / right,
14176
+ "%": (left, right) => left % right,
14177
+ "&": (left, right) => left & right,
14178
+ "|": (left, right) => left | right,
14179
+ "^": (left, right) => left ^ right,
14180
+ "<<": (left, right) => left << (right & 127n),
14181
+ ">>": (left, right) => left >> (right & 127n),
14182
+ "==": (left, right) =>
14183
+ // two string literals will compare unequal, becuase string
14184
+ // equality is object equality.
14185
+ typeof left === "string" ? false : left === right,
14186
+ "!=": (left, right) => typeof left === "string" ? true : left !== right,
14187
+ "<=": (left, right) => left <= right,
14188
+ ">=": (left, right) => left >= right,
14189
+ "<": (left, right) => left < right,
14190
+ ">": (left, right) => left > right,
14191
+ as: null,
14192
+ instanceof: null,
14193
+ has: null,
14194
+ };
13612
14195
  function optimizeNode(state, node) {
13613
14196
  switch (node.type) {
13614
14197
  case "UnaryExpression": {
@@ -13623,29 +14206,17 @@ function optimizeNode(state, node) {
13623
14206
  break;
13624
14207
  case "-":
13625
14208
  if (type === "Number" || type === "Long") {
13626
- return {
13627
- ...arg,
13628
- value: -arg.value,
13629
- raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
13630
- };
14209
+ return replacementLiteral(arg, -arg.value, type);
13631
14210
  }
13632
14211
  break;
13633
14212
  case "!":
13634
14213
  case "~":
13635
14214
  {
13636
- let value;
13637
14215
  if (type === "Number" || type === "Long") {
13638
- value = -arg.value - 1;
14216
+ return replacementLiteral(arg, ~BigInt(arg.value), type);
13639
14217
  }
13640
- else if (type === "Boolean" && node.operator == "!") {
13641
- value = !arg.value;
13642
- }
13643
- if (value !== undefined) {
13644
- return {
13645
- ...arg,
13646
- value,
13647
- raw: value.toString() + (type === "Long" ? "l" : ""),
13648
- };
14218
+ if (type === "Boolean" && node.operator == "!") {
14219
+ return replacementLiteral(arg, !arg.value, type);
13649
14220
  }
13650
14221
  }
13651
14222
  break;
@@ -13653,27 +14224,6 @@ function optimizeNode(state, node) {
13653
14224
  break;
13654
14225
  }
13655
14226
  case "BinaryExpression": {
13656
- const operators = {
13657
- "+": (left, right) => left + right,
13658
- "-": (left, right) => left - right,
13659
- "*": (left, right) => left * right,
13660
- "/": (left, right) => Math.trunc(left / right),
13661
- "%": (left, right) => left % right,
13662
- "&": (left, right, type) => type === "Number" ? left & right : null,
13663
- "|": (left, right, type) => type === "Number" ? left | right : null,
13664
- "^": (left, right, type) => type === "Number" ? left ^ right : null,
13665
- "<<": (left, right, type) => type === "Number" ? left << right : null,
13666
- ">>": (left, right, type) => type === "Number" ? left >> right : null,
13667
- "==": (left, right) => left == right,
13668
- "!=": (left, right) => left != right,
13669
- "<=": (left, right) => left <= right,
13670
- ">=": (left, right) => left >= right,
13671
- "<": (left, right) => left < right,
13672
- ">": (left, right) => left > right,
13673
- as: null,
13674
- instanceof: null,
13675
- has: null,
13676
- };
13677
14227
  const op = operators[node.operator];
13678
14228
  if (op) {
13679
14229
  const [left, left_type] = getNodeValue(node.left);
@@ -13681,23 +14231,22 @@ function optimizeNode(state, node) {
13681
14231
  if (!left || !right)
13682
14232
  break;
13683
14233
  let value = null;
13684
- if (left_type != right_type ||
13685
- (left_type != "Number" && left_type != "Long")) {
14234
+ let type;
14235
+ if ((left_type != "Number" && left_type != "Long") ||
14236
+ left_type != right_type) {
13686
14237
  if (node.operator !== "==" && node.operator !== "!=") {
13687
14238
  break;
13688
14239
  }
13689
14240
  value = operators[node.operator](left.value, right.value);
14241
+ type = "Boolean";
13690
14242
  }
13691
14243
  else {
13692
- value = op(left.value, right.value, left_type);
14244
+ type = left_type;
14245
+ value = op(BigInt(left.value), BigInt(right.value));
13693
14246
  }
13694
14247
  if (value === null)
13695
14248
  break;
13696
- return {
13697
- ...left,
13698
- value,
13699
- raw: value.toString() + (left_type === "Long" ? "l" : ""),
13700
- };
14249
+ return replacementLiteral(left, value, type);
13701
14250
  }
13702
14251
  break;
13703
14252
  }
@@ -13707,7 +14256,8 @@ function optimizeNode(state, node) {
13707
14256
  break;
13708
14257
  const falsy = left.value === false ||
13709
14258
  left.value === null ||
13710
- (left.value === 0 && (left_type === "Number" || left_type === "Long"));
14259
+ ((left_type === "Number" || left_type === "Long") &&
14260
+ (left.value === 0 || left.value === 0n));
13711
14261
  if (falsy === (node.operator === "&&")) {
13712
14262
  return left;
13713
14263
  }
@@ -13748,9 +14298,7 @@ function evaluateFunction(state, func, args) {
13748
14298
  const paramValues = args &&
13749
14299
  Object.fromEntries(func.params.map((p, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(p), args[i]]));
13750
14300
  let ret = null;
13751
- const body = args
13752
- ? JSON.parse(JSON.stringify(func.body))
13753
- : func.body;
14301
+ const body = args ? cloneDeep(func.body) : func.body;
13754
14302
  try {
13755
14303
  traverseAst(body, (node) => {
13756
14304
  switch (node.type) {
@@ -13801,12 +14349,10 @@ function markFunctionCalled(state, func) {
13801
14349
  (0,external_util_cjs_namespaceObject.pushUnique)(state.calledFunctions[func.id.name], func);
13802
14350
  }
13803
14351
  async function optimizeMonkeyC(fnMap, barrelList, config) {
13804
- const state = {
13805
- ...(await analyze(fnMap, barrelList, config)),
13806
- localsStack: [{}],
13807
- exposed: {},
13808
- calledFunctions: {},
13809
- };
14352
+ const state = (await analyze(fnMap, barrelList, config));
14353
+ state.localsStack = [{}];
14354
+ state.calledFunctions = {};
14355
+ state.usedByName = {};
13810
14356
  const replace = (node, old) => {
13811
14357
  if (node === false || node === null)
13812
14358
  return node;
@@ -13895,6 +14441,58 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
13895
14441
  f.type == "FunctionDeclaration" &&
13896
14442
  maybeCalled(f.node))) ||
13897
14443
  (sc.superClass && checkInherited(sc, name))));
14444
+ const renamer = (idnode) => {
14445
+ const ident = idnode.type === "Identifier" ? idnode : idnode.left;
14446
+ const locals = topLocals();
14447
+ const { map } = locals;
14448
+ if (map) {
14449
+ const declName = ident.name;
14450
+ const name = renameVariable(state, locals, declName);
14451
+ if (name) {
14452
+ const [, results] = state.lookupValue(ident);
14453
+ if (!results) {
14454
+ throw new Error(`Didn't find local ${declName} which needed renaming`);
14455
+ }
14456
+ if (results.length !== 1) {
14457
+ throw new Error(`Lookup of local ${declName} found more than one result`);
14458
+ }
14459
+ const parent = results[0].parent;
14460
+ if (!parent) {
14461
+ throw new Error(`No parent in lookup of local ${declName}`);
14462
+ }
14463
+ const decls = parent.decls;
14464
+ if (!decls || !(0,external_api_cjs_namespaceObject.hasProperty)(decls, declName)) {
14465
+ throw new Error(`Missing decls in lookup of local ${declName}`);
14466
+ }
14467
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(decls, name)) {
14468
+ throw new Error(`While renaming ${declName} to ${name}, there was already a variable ${name}`);
14469
+ }
14470
+ if (decls[declName].length === 1) {
14471
+ decls[name] = decls[declName];
14472
+ delete decls[declName];
14473
+ }
14474
+ else {
14475
+ let i = decls[declName].length;
14476
+ while (i--) {
14477
+ const decl = decls[declName][i];
14478
+ if (decl === idnode ||
14479
+ (decl.type === "VariableDeclarator" && decl.node.id === idnode)) {
14480
+ decls[declName].splice(i, 1);
14481
+ decls[name] = [decl];
14482
+ break;
14483
+ }
14484
+ }
14485
+ if (i < 0) {
14486
+ throw new Error(`While renaming ${declName} to ${name}: Didn't find original declaration`);
14487
+ }
14488
+ }
14489
+ ident.name = name;
14490
+ }
14491
+ else {
14492
+ map[declName] = true;
14493
+ }
14494
+ }
14495
+ };
13898
14496
  state.pre = (node) => {
13899
14497
  switch (node.type) {
13900
14498
  case "ConditionalExpression":
@@ -13915,7 +14513,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
13915
14513
  result = !!value.value;
13916
14514
  }
13917
14515
  if (result !== null) {
13918
- node.test = { type: "Literal", value: result };
14516
+ node.test = {
14517
+ type: "Literal",
14518
+ value: result,
14519
+ raw: result.toString(),
14520
+ };
13919
14521
  if (node.type === "IfStatement" ||
13920
14522
  node.type === "ConditionalExpression") {
13921
14523
  return [result ? "consequent" : "alternate"];
@@ -13934,7 +14536,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
13934
14536
  return null;
13935
14537
  }
13936
14538
  case "EnumDeclaration":
13937
- return false;
14539
+ return [];
13938
14540
  case "ForStatement": {
13939
14541
  const map = topLocals().map;
13940
14542
  if (map) {
@@ -13943,43 +14545,13 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
13943
14545
  break;
13944
14546
  }
13945
14547
  case "VariableDeclarator": {
13946
- const locals = topLocals();
13947
- const { map } = locals;
13948
- if (map) {
13949
- const declName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id);
13950
- const name = renameVariable(state, locals, declName);
13951
- if (name) {
13952
- if (node.id.type === "Identifier") {
13953
- node.id.name = name;
13954
- }
13955
- else {
13956
- node.id.left.name = name;
13957
- }
13958
- }
13959
- else {
13960
- map[declName] = true;
13961
- }
13962
- }
14548
+ renamer(node.id);
13963
14549
  return ["init"];
13964
14550
  }
13965
14551
  case "CatchClause":
13966
14552
  if (node.param) {
13967
14553
  state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
13968
- const locals = topLocals();
13969
- const map = locals.map;
13970
- const declName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(node.param);
13971
- const name = renameVariable(state, locals, declName);
13972
- if (name) {
13973
- if (node.param.type === "Identifier") {
13974
- node.param.name = name;
13975
- }
13976
- else {
13977
- node.param.left.name = name;
13978
- }
13979
- }
13980
- else {
13981
- map[declName] = true;
13982
- }
14554
+ renamer(node.param);
13983
14555
  return ["body"];
13984
14556
  }
13985
14557
  break;
@@ -13995,14 +14567,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
13995
14567
  break;
13996
14568
  case "UnaryExpression":
13997
14569
  if (node.operator == ":") {
13998
- // If we produce a Symbol, for a given name,
13999
- // its possible that someone uses that symbol
14000
- // indirectly, so we can't remove any enums or
14001
- // constants with that name (we can still replace
14002
- // uses of those constants though).
14003
- state.exposed[node.argument.name] = true;
14004
- // In any case, we can't replace *this* use of the
14005
- // symbol with its value...
14570
+ // node.argument is not a normal identifier.
14571
+ // don't visit it.
14006
14572
  return [];
14007
14573
  }
14008
14574
  break;
@@ -14014,29 +14580,73 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14014
14580
  if (typeof name === "string") {
14015
14581
  node.name = name;
14016
14582
  }
14583
+ const [, results] = state.lookupValue(node);
14584
+ if (results) {
14585
+ if (results.length !== 1 || results[0].results.length !== 1) {
14586
+ throw new Error(`Local ${node.name} had multiple lookup results`);
14587
+ }
14588
+ const parent = results[0].parent;
14589
+ if (!parent) {
14590
+ throw new Error(`Local ${node.name} had no parent`);
14591
+ }
14592
+ const decl = results[0].results[0];
14593
+ if (parent.type === "FunctionDeclaration" ||
14594
+ decl.type !== "VariableDeclarator") {
14595
+ // we can't optimize away function or catch parameters
14596
+ return [];
14597
+ }
14598
+ if (parent.type !== "BlockStatement") {
14599
+ throw new Error(`Local ${node.name} was not declared at block scope(??)`);
14600
+ }
14601
+ decl.used = true;
14602
+ }
14017
14603
  }
14018
14604
  }
14019
14605
  if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, node.name)) {
14020
14606
  if (!lookupAndReplace(node)) {
14021
- state.exposed[node.name] = true;
14607
+ state.usedByName[node.name] = true;
14022
14608
  }
14023
14609
  }
14024
14610
  return [];
14025
14611
  }
14026
- case "MemberExpression":
14027
- if (node.property.type === "Identifier" && !node.computed) {
14028
- if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, node.property.name)) {
14612
+ case "MemberExpression": {
14613
+ const property = (0,external_api_cjs_namespaceObject.isLookupCandidate)(node);
14614
+ if (property) {
14615
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, property.name)) {
14029
14616
  if (lookupAndReplace(node)) {
14030
14617
  return false;
14031
14618
  }
14032
14619
  else {
14033
- state.exposed[node.property.name] = true;
14620
+ state.usedByName[property.name] = true;
14034
14621
  }
14035
14622
  }
14036
14623
  // Don't optimize the property.
14037
14624
  return ["object"];
14038
14625
  }
14039
14626
  break;
14627
+ }
14628
+ case "AssignmentExpression":
14629
+ case "UpdateExpression": {
14630
+ const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
14631
+ if (lhs.type === "Identifier") {
14632
+ const map = topLocals().map;
14633
+ if (map) {
14634
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(map, lhs.name)) {
14635
+ const name = map[lhs.name];
14636
+ if (typeof name === "string") {
14637
+ lhs.name = name;
14638
+ }
14639
+ }
14640
+ }
14641
+ }
14642
+ else if (lhs.type === "MemberExpression") {
14643
+ state.traverse(lhs.object);
14644
+ if (lhs.computed) {
14645
+ state.traverse(lhs.property);
14646
+ }
14647
+ }
14648
+ return node.type === "AssignmentExpression" ? ["right"] : [];
14649
+ }
14040
14650
  case "BlockStatement": {
14041
14651
  const map = topLocals().map;
14042
14652
  if (map) {
@@ -14052,7 +14662,11 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14052
14662
  node.params &&
14053
14663
  node.params.forEach((p) => (map[(0,external_api_cjs_namespaceObject.variableDeclarationName)(p)] = true));
14054
14664
  state.localsStack.push({ node, map });
14055
- const [parent] = state.stack.slice(-2);
14665
+ const [parent, self] = state.stack.slice(-2);
14666
+ if (state.currentFunction) {
14667
+ throw new Error(`Nested functions: ${self.fullName} was activated during processing of ${state.currentFunction.fullName}`);
14668
+ }
14669
+ state.currentFunction = self;
14056
14670
  if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
14057
14671
  let used = false;
14058
14672
  if (node.id.name == "initialize") {
@@ -14079,10 +14693,23 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14079
14693
  return replace(opt, node);
14080
14694
  }
14081
14695
  switch (node.type) {
14696
+ case "FunctionDeclaration":
14697
+ if (!state.currentFunction) {
14698
+ throw new Error(`Finished function ${state.stack.slice(-1)[0].fullName}, but it was not marked current`);
14699
+ }
14700
+ state.currentFunction.info = state.currentFunction.next_info;
14701
+ delete state.currentFunction.next_info;
14702
+ delete state.currentFunction;
14703
+ break;
14082
14704
  case "BlockStatement":
14083
14705
  if (node.body.length === 1 && node.body[0].type === "BlockStatement") {
14084
14706
  node.body.splice(0, 1, ...node.body[0].body);
14085
14707
  }
14708
+ // fall through
14709
+ case "ForStatement":
14710
+ if (locals.map) {
14711
+ cleanupUnusedVars(state, node);
14712
+ }
14086
14713
  break;
14087
14714
  case "ConditionalExpression":
14088
14715
  case "IfStatement":
@@ -14115,17 +14742,20 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14115
14742
  return replace(optimizeCall(state, node.argument, node), node.argument);
14116
14743
  }
14117
14744
  break;
14745
+ case "NewExpression":
14746
+ if (state.currentFunction) {
14747
+ const [, results] = state.lookup(node.callee);
14748
+ if (results) {
14749
+ recordCalledFuncs(state.currentFunction, findCalleesForNew(results));
14750
+ }
14751
+ else {
14752
+ recordModifiedUnknown(state.currentFunction);
14753
+ }
14754
+ }
14755
+ break;
14118
14756
  case "CallExpression": {
14119
14757
  return replace(optimizeCall(state, node, null), node);
14120
14758
  }
14121
- case "AssignmentExpression":
14122
- if (node.operator === "=" &&
14123
- node.left.type === "Identifier" &&
14124
- node.right.type === "Identifier" &&
14125
- node.left.name === node.right.name) {
14126
- return { type: "Literal", value: null, raw: "null" };
14127
- }
14128
- break;
14129
14759
  case "VariableDeclaration": {
14130
14760
  const locals = topLocals();
14131
14761
  if (locals.map &&
@@ -14138,10 +14768,10 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14138
14768
  while (i < node.declarations.length) {
14139
14769
  const decl = declarations[i++];
14140
14770
  if (decl.init && decl.init.type === "CallExpression") {
14141
- const inlined = optimizeCall(state, decl.init, decl);
14771
+ const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
14142
14772
  if (!inlined)
14143
14773
  continue;
14144
- if (inlined.type != "BlockStatement") {
14774
+ if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
14145
14775
  throw new Error("Unexpected inlined result");
14146
14776
  }
14147
14777
  if (!results) {
@@ -14199,6 +14829,32 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14199
14829
  }
14200
14830
  }
14201
14831
  break;
14832
+ case "AssignmentExpression":
14833
+ if (node.operator === "=" &&
14834
+ node.left.type === "Identifier" &&
14835
+ node.right.type === "Identifier" &&
14836
+ node.left.name === node.right.name) {
14837
+ return { type: "Literal", value: null, raw: "null" };
14838
+ }
14839
+ // fall through;
14840
+ case "UpdateExpression":
14841
+ if (state.currentFunction) {
14842
+ const lhs = node.type === "AssignmentExpression" ? node.left : node.argument;
14843
+ const [, results] = state.lookup(lhs);
14844
+ if (results) {
14845
+ recordModifiedDecls(state.currentFunction, results);
14846
+ }
14847
+ else {
14848
+ const id = lhs.type === "Identifier" ? lhs : (0,external_api_cjs_namespaceObject.isLookupCandidate)(lhs);
14849
+ if (id) {
14850
+ recordModifiedName(state.currentFunction, id.name);
14851
+ }
14852
+ else {
14853
+ recordModifiedUnknown(state.currentFunction);
14854
+ }
14855
+ }
14856
+ }
14857
+ break;
14202
14858
  }
14203
14859
  return null;
14204
14860
  };
@@ -14206,13 +14862,16 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14206
14862
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
14207
14863
  });
14208
14864
  state.calledFunctions = {};
14209
- state.exposed = {};
14865
+ state.exposed = state.nextExposed;
14866
+ state.nextExposed = {};
14210
14867
  Object.values(fnMap).forEach((f) => {
14211
14868
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
14212
14869
  });
14870
+ state.exposed = state.nextExposed;
14871
+ state.nextExposed = {};
14213
14872
  delete state.pre;
14214
14873
  delete state.post;
14215
- state.allFunctions.forEach((fn) => sizeBasedPRE(state, fn));
14874
+ Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
14216
14875
  const cleanup = (node) => {
14217
14876
  switch (node.type) {
14218
14877
  case "ThisExpression":
@@ -14222,7 +14881,8 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14222
14881
  if (node.members.every((m) => {
14223
14882
  const name = "name" in m ? m.name : m.id.name;
14224
14883
  return ((0,external_api_cjs_namespaceObject.hasProperty)(state.index, name) &&
14225
- !(0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, name));
14884
+ !(0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, name) &&
14885
+ !(0,external_api_cjs_namespaceObject.hasProperty)(state.usedByName, name));
14226
14886
  })) {
14227
14887
  node.enumType = [
14228
14888
  ...new Set(node.members.map((m) => {
@@ -14265,7 +14925,9 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14265
14925
  case "VariableDeclaration": {
14266
14926
  node.declarations = node.declarations.filter((d) => {
14267
14927
  const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(d.id);
14268
- return (!(0,external_api_cjs_namespaceObject.hasProperty)(state.index, name) || (0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, name));
14928
+ return (!(0,external_api_cjs_namespaceObject.hasProperty)(state.index, name) ||
14929
+ (0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, name) ||
14930
+ (0,external_api_cjs_namespaceObject.hasProperty)(state.usedByName, name));
14269
14931
  });
14270
14932
  if (!node.declarations.length) {
14271
14933
  return false;
@@ -14298,7 +14960,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14298
14960
  }
14299
14961
  return null;
14300
14962
  };
14301
- Object.values(fnMap).forEach((f) => {
14963
+ Object.entries(fnMap).forEach(([name, f]) => {
14302
14964
  traverseAst(f.ast, undefined, (node) => {
14303
14965
  const ret = cleanup(node);
14304
14966
  if (ret === false) {
@@ -14306,16 +14968,15 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14306
14968
  }
14307
14969
  return ret;
14308
14970
  });
14971
+ if (state.config && state.config.checkBuildPragmas) {
14972
+ pragmaChecker(state, f.ast, state.diagnostics?.[name]);
14973
+ }
14309
14974
  });
14310
14975
  return state.diagnostics;
14311
14976
  }
14312
14977
  function optimizeCall(state, node, context) {
14313
14978
  const [name, results] = state.lookupNonlocal(node.callee);
14314
- const callees = results &&
14315
- results
14316
- .map((r) => r.results)
14317
- .flat()
14318
- .filter((c) => c.type === "FunctionDeclaration");
14979
+ const callees = results ? findCallees(results) : null;
14319
14980
  if (!callees || !callees.length) {
14320
14981
  const n = name ||
14321
14982
  ("name" in node.callee && node.callee.name) ||
@@ -14324,14 +14985,24 @@ function optimizeCall(state, node, context) {
14324
14985
  "name" in node.callee.property &&
14325
14986
  node.callee.property.name);
14326
14987
  if (n) {
14327
- state.exposed[n] = true;
14988
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(state.allFunctions, n)) {
14989
+ if (state.currentFunction) {
14990
+ recordCalledFuncs(state.currentFunction, state.allFunctions[n]);
14991
+ }
14992
+ state.allFunctions[n].forEach((fn) => markFunctionCalled(state, fn.node));
14993
+ }
14328
14994
  }
14329
- else {
14330
- // There are unnamed CallExpressions, such as new [size]
14331
- // So there's nothing to do here.
14995
+ else if (state.currentFunction) {
14996
+ // I don't think this can happen: foo[x](args)
14997
+ // doesn't parse, so you can't even do things like
14998
+ // $.Toybox.Lang[:format]("fmt", [])
14999
+ recordModifiedUnknown(state.currentFunction);
14332
15000
  }
14333
15001
  return null;
14334
15002
  }
15003
+ if (state.currentFunction) {
15004
+ recordCalledFuncs(state.currentFunction, callees);
15005
+ }
14335
15006
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
14336
15007
  const callee = callees[0].node;
14337
15008
  if (!context &&
@@ -14354,120 +15025,6 @@ function optimizeCall(state, node, context) {
14354
15025
  return null;
14355
15026
  }
14356
15027
 
14357
- ;// CONCATENATED MODULE: ./src/pragma-checker.ts
14358
-
14359
-
14360
- function pragmaChecker(ast, diagnostics) {
14361
- const comments = ast.comments;
14362
- if (!comments)
14363
- return;
14364
- diagnostics = diagnostics
14365
- ?.slice()
14366
- .sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
14367
- let diagIndex = 0;
14368
- let index = -1;
14369
- let comment;
14370
- let matchers;
14371
- const next = () => {
14372
- while (++index < comments.length) {
14373
- comment = comments[index];
14374
- let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
14375
- if (!match)
14376
- continue;
14377
- const kind = match[1];
14378
- let str = match[2];
14379
- matchers = [];
14380
- while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
14381
- matchers.push({ kind, quote: match[1], needle: match[2] });
14382
- str = str.substring(match[0].length);
14383
- if (!str.length)
14384
- break;
14385
- }
14386
- if (!str.length)
14387
- break;
14388
- if (!matchers.length) {
14389
- match = str.match(/^(\S+)\s+$/);
14390
- if (match) {
14391
- matchers.push({ kind, quote: '"', needle: match[1] });
14392
- break;
14393
- }
14394
- }
14395
- throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
14396
- }
14397
- };
14398
- const matcher = (quote, needle, haystack) => {
14399
- if (quote == '"') {
14400
- return haystack.includes(needle);
14401
- }
14402
- const re = new RegExp(needle.replace(/@(\d+)/g, "(pre_)?$1(_\\d+)?"));
14403
- return re.test(haystack);
14404
- };
14405
- next();
14406
- traverseAst(ast, (node) => {
14407
- if (index >= comments.length)
14408
- return false;
14409
- if (node.start && node.start >= (comment.end || Infinity)) {
14410
- const { kind, quote, needle } = matchers.shift();
14411
- if (kind === "match") {
14412
- const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " ");
14413
- if (!matcher(quote, needle, haystack)) {
14414
- matcher(quote, needle, haystack);
14415
- throw new Error(`Didn't find '${needle}' in '${haystack}' at ${comment.loc.source}:${comment.loc.start.line}`);
14416
- }
14417
- }
14418
- else if (kind === "expect") {
14419
- const locCmp = (a, b) => {
14420
- if (!b)
14421
- return -1;
14422
- if (a.start.line < b.start.line)
14423
- return -1;
14424
- if (a.start.line === b.start.line &&
14425
- a.start.column < b.start.column) {
14426
- return -1;
14427
- }
14428
- if (a.end.line > b.end.line)
14429
- return 1;
14430
- if (a.end.line === b.end.line && a.end.column >= b.end.column) {
14431
- return 1;
14432
- }
14433
- return 0;
14434
- };
14435
- let found = false;
14436
- if (diagnostics) {
14437
- while (true) {
14438
- if (diagIndex >= diagnostics.length) {
14439
- diagnostics = null;
14440
- break;
14441
- }
14442
- const diag = diagnostics[diagIndex];
14443
- const cmp = locCmp(diag.loc, node.loc);
14444
- if (cmp > 0) {
14445
- break;
14446
- }
14447
- diagIndex++;
14448
- if (cmp < 0)
14449
- continue;
14450
- if (matcher(quote, needle, diag.message)) {
14451
- found = true;
14452
- diag.type = "INFO";
14453
- }
14454
- }
14455
- }
14456
- if (!found) {
14457
- throw new Error(`Missing error message '${needle} at ${comment.loc.source}:${comment.loc.start.line}`);
14458
- }
14459
- }
14460
- if (matchers.length) {
14461
- // if we're checking a series of nodes, we need
14462
- // to skip over this one.
14463
- return false;
14464
- }
14465
- next();
14466
- }
14467
- return null;
14468
- });
14469
- }
14470
-
14471
15028
  ;// CONCATENATED MODULE: ./src/optimizer.ts
14472
15029
 
14473
15030
 
@@ -14905,22 +15462,19 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
14905
15462
  // the oldest optimized file, we don't need to regenerate
14906
15463
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
14907
15464
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
14908
- if (source_time < opt_time && 1656718632916 < opt_time) {
15465
+ if (source_time < opt_time && 1657744666168 < opt_time) {
14909
15466
  return { hasTests, diagnostics: prevDiagnostics };
14910
15467
  }
14911
15468
  }
14912
15469
  await promises_namespaceObject.rm(output, { recursive: true, force: true });
14913
15470
  await promises_namespaceObject.mkdir(output, { recursive: true });
14914
15471
  const diagnostics = await optimizeMonkeyC(fnMap, Object.keys(buildConfig.barrelMap || {}), config);
14915
- return Promise.all(Object.entries(fnMap).map(async ([inFile, info]) => {
15472
+ return Promise.all(Object.values(fnMap).map(async (info) => {
14916
15473
  const name = info.output;
14917
15474
  const dir = external_path_.dirname(name);
14918
15475
  await promises_namespaceObject.mkdir(dir, { recursive: true });
14919
15476
  const opt_source = (0,external_api_cjs_namespaceObject.formatAst)(info.ast, info.monkeyCSource);
14920
15477
  await promises_namespaceObject.writeFile(name, opt_source);
14921
- if (config.checkBuildPragmas) {
14922
- pragmaChecker(info.ast, diagnostics?.[inFile]);
14923
- }
14924
15478
  return info.hasTests;
14925
15479
  })).then((results) => {
14926
15480
  const hasTests = results.some((v) => v);