@markw65/monkeyc-optimizer 1.0.19 → 1.0.22

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.
@@ -10784,11 +10784,11 @@ function launchSimulator() {
10784
10784
  child.unref();
10785
10785
  });
10786
10786
  }
10787
- function simulateProgram(prg, device, test) {
10787
+ function simulateProgram(prg, device, test = false, logger) {
10788
10788
  const args = [prg, device];
10789
10789
  if (test)
10790
10790
  args.push("-t");
10791
- return (0,external_sdk_util_cjs_namespaceObject.getSdkPath)().then((sdk) => (0,external_util_cjs_namespaceObject.spawnByLine)(external_path_.resolve(sdk, "bin", "monkeydo"), args, (line) => console.log(line)).then(() => { }));
10791
+ return (0,external_sdk_util_cjs_namespaceObject.getSdkPath)().then((sdk) => (0,external_util_cjs_namespaceObject.spawnByLine)(external_path_.resolve(sdk, "bin", "monkeydo"), args, logger || ((line) => console.log(line))).then(() => { }));
10792
10792
  }
10793
10793
 
10794
10794
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
@@ -10853,23 +10853,32 @@ function getArgSafety(state, func, args, requireAll) {
10853
10853
  // determine whether decl might be changed by a function call
10854
10854
  // or assignment during the evaluation of FunctionStateNode.
10855
10855
  const getSafety = (decl) => {
10856
- // enums are constant, they cant change
10857
- if (decl.type === "EnumStringMember")
10858
- return true;
10859
- if (decl.type === "VariableDeclarator") {
10860
- // constants also can't change
10861
- if (decl.node.kind === "const")
10856
+ switch (decl.type) {
10857
+ // enums are constant, they cant change
10858
+ case "EnumStringMember":
10862
10859
  return true;
10863
- // if decl is a local, it also can't be changed
10864
- // by a call to another function.
10865
- for (let i = 0;; i++) {
10866
- if (!state.stack[i] || decl.stack[i] !== state.stack[i])
10867
- return false;
10868
- if (state.stack[i].type === "FunctionDeclaration")
10860
+ case "VariableDeclarator": {
10861
+ // constants also can't change
10862
+ if (decl.node.kind === "const")
10869
10863
  return true;
10864
+ // if decl is a local, it also can't be changed
10865
+ // by a call to another function.
10866
+ for (let i = 0;; i++) {
10867
+ if (!state.stack[i] || decl.stack[i] !== state.stack[i])
10868
+ return false;
10869
+ if (state.stack[i].type === "FunctionDeclaration")
10870
+ return true;
10871
+ }
10870
10872
  }
10873
+ case "Identifier":
10874
+ case "BinaryExpression":
10875
+ // This is a parameter of the calling function.
10876
+ // It also can't be changed during the execution
10877
+ // of the inlined function
10878
+ return true;
10879
+ default:
10880
+ return null;
10871
10881
  }
10872
- return null;
10873
10882
  };
10874
10883
  const safeArgs = [];
10875
10884
  let allSafe = true;
@@ -10881,11 +10890,13 @@ function getArgSafety(state, func, args, requireAll) {
10881
10890
  case "Identifier":
10882
10891
  case "MemberExpression": {
10883
10892
  const [, results] = state.lookup(arg);
10884
- if (!results || results.length !== 1) {
10893
+ if (!results ||
10894
+ results.length !== 1 ||
10895
+ results[0].results.length !== 1) {
10885
10896
  safeArgs.push(null);
10886
10897
  return !requireAll;
10887
10898
  }
10888
- const safety = getSafety(results[0]);
10899
+ const safety = getSafety(results[0].results[0]);
10889
10900
  safeArgs.push(safety);
10890
10901
  if (!safety) {
10891
10902
  allSafe = false;
@@ -10977,27 +10988,23 @@ function inliningLooksUseful(func, node) {
10977
10988
  }
10978
10989
  return false;
10979
10990
  }
10980
- var InlineStatus;
10981
- (function (InlineStatus) {
10982
- InlineStatus[InlineStatus["Never"] = 0] = "Never";
10983
- InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
10984
- InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
10985
- })(InlineStatus || (InlineStatus = {}));
10986
10991
  function inlineRequested(state, func) {
10987
10992
  const excludeAnnotations = (func.node.loc?.source &&
10988
10993
  state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
10989
10994
  {};
10990
10995
  if (func.node.attrs &&
10991
- func.node.attrs.attrs &&
10992
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
10996
+ func.node.attrs.attributes &&
10997
+ func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
10993
10998
  (attr.argument.name === "inline" ||
10994
10999
  (attr.argument.name.startsWith("inline_") &&
10995
- (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))))) {
11000
+ !(0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))))) {
10996
11001
  return true;
10997
11002
  }
10998
11003
  return false;
10999
11004
  }
11000
11005
  function shouldInline(state, func, call, context) {
11006
+ if (state.inlining)
11007
+ return false;
11001
11008
  let autoInline = false;
11002
11009
  let inlineAsExpression = false;
11003
11010
  const args = call.arguments;
@@ -11030,6 +11037,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11030
11037
  let failed = false;
11031
11038
  const pre = state.pre;
11032
11039
  const post = state.post;
11040
+ state.inlining = true;
11033
11041
  try {
11034
11042
  state.pre = (node) => {
11035
11043
  if (failed)
@@ -11075,7 +11083,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11075
11083
  };
11076
11084
  state.post = (node) => {
11077
11085
  if (failed)
11078
- return null;
11086
+ return post(node, state);
11079
11087
  let replacement = null;
11080
11088
  switch (node.type) {
11081
11089
  case "Identifier": {
@@ -11092,12 +11100,13 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11092
11100
  if (!replacement) {
11093
11101
  failed = true;
11094
11102
  inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11095
- return null;
11103
+ return post(node, state);
11096
11104
  }
11097
11105
  break;
11098
11106
  }
11099
11107
  }
11100
- return post(replacement || node, state) || replacement;
11108
+ const ret = post(replacement || node, state);
11109
+ return ret === false || ret ? ret : replacement;
11101
11110
  };
11102
11111
  let ret = state.traverse(root);
11103
11112
  if (failed) {
@@ -11116,6 +11125,7 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11116
11125
  finally {
11117
11126
  state.pre = pre;
11118
11127
  state.post = post;
11128
+ delete state.inlining;
11119
11129
  }
11120
11130
  }
11121
11131
  function unused(expression, top) {
@@ -11157,7 +11167,7 @@ function unused(expression, top) {
11157
11167
  },
11158
11168
  ];
11159
11169
  }
11160
- function diagnostic(state, loc, message) {
11170
+ function diagnostic(state, loc, message, type = "INFO") {
11161
11171
  if (!loc || !loc.source)
11162
11172
  return;
11163
11173
  const source = loc.source;
@@ -11173,7 +11183,7 @@ function diagnostic(state, loc, message) {
11173
11183
  if (message) {
11174
11184
  if (index < 0)
11175
11185
  index = diags.length;
11176
- diags[index] = { type: "INFO", loc, message };
11186
+ diags[index] = { type, loc, message };
11177
11187
  }
11178
11188
  else if (index >= 0) {
11179
11189
  diags.splice(index, 1);
@@ -11292,23 +11302,25 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11292
11302
  // With a bit more work, we could find the guaranteed shortest
11293
11303
  // reference, and then use this to optimize *all* symbols, not
11294
11304
  // just fix inlined ones.
11295
- if (current &&
11296
- current.length === original.length &&
11297
- current.every((item, index) => item == original[index])) {
11305
+ if (current && (0,external_api_cjs_namespaceObject.sameLookupResult)(original, current)) {
11298
11306
  return lookupNode;
11299
11307
  }
11300
11308
  const node = lookupNode.type === "Identifier"
11301
11309
  ? lookupNode
11302
11310
  : lookupNode.property;
11303
- if (original.length === 1 && original[0].type === "EnumStringMember") {
11304
- return applyTypeIfNeeded(original[0].init);
11311
+ if (original.length === 1 &&
11312
+ original[0].results.length === 1 &&
11313
+ original[0].results[0].type === "EnumStringMember") {
11314
+ return applyTypeIfNeeded(original[0].results[0].init);
11305
11315
  }
11306
- const prefixes = original.map((sn) => {
11316
+ const prefixes = original
11317
+ .map((lookupDef) => lookupDef.results.map((sn) => {
11307
11318
  if ((0,external_api_cjs_namespaceObject.isStateNode)(sn) && sn.fullName) {
11308
11319
  return sn.fullName;
11309
11320
  }
11310
11321
  return "";
11311
- });
11322
+ }))
11323
+ .flat();
11312
11324
  if (prefixes.length &&
11313
11325
  prefixes[0].startsWith("$.") &&
11314
11326
  prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
@@ -11318,9 +11330,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11318
11330
  if (found)
11319
11331
  return current;
11320
11332
  const [, results] = state.lookup(current);
11321
- if (results &&
11322
- results.length === original.length &&
11323
- results.every((result, i) => result === original[i])) {
11333
+ if (results && (0,external_api_cjs_namespaceObject.sameLookupResult)(original, results)) {
11324
11334
  found = true;
11325
11335
  return current;
11326
11336
  }
@@ -11360,6 +11370,85 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11360
11370
  return null;
11361
11371
  }
11362
11372
 
11373
+ ;// CONCATENATED MODULE: ./src/visitor.ts
11374
+
11375
+ function visitReferences(state, ast, name, defn, callback) {
11376
+ const checkResults = ([name, results], node) => {
11377
+ if (name && results) {
11378
+ if (!defn || (0,external_api_cjs_namespaceObject.sameLookupResult)(results, defn)) {
11379
+ if (callback(node, results, false) === false) {
11380
+ return [];
11381
+ }
11382
+ }
11383
+ }
11384
+ else if (defn === false) {
11385
+ if (callback(node, [], results === null) === false) {
11386
+ return [];
11387
+ }
11388
+ }
11389
+ return null;
11390
+ };
11391
+ state.pre = (node) => {
11392
+ switch (node.type) {
11393
+ case "AttributeList":
11394
+ return [];
11395
+ case "UnaryExpression":
11396
+ // a bare symbol isn't a reference
11397
+ if (node.operator === ":")
11398
+ return [];
11399
+ break;
11400
+ case "BinaryExpression":
11401
+ /*
11402
+ * `expr has :symbol` can be treated as a reference
11403
+ * to expr.symbol.
11404
+ */
11405
+ if (node.operator === "has") {
11406
+ if (node.right.type === "UnaryExpression" &&
11407
+ node.right.operator === ":") {
11408
+ if (!name || node.right.argument.name === name) {
11409
+ return checkResults(state.lookup({
11410
+ type: "MemberExpression",
11411
+ object: node.left,
11412
+ property: node.right.argument,
11413
+ computed: false,
11414
+ }), node.right.argument);
11415
+ }
11416
+ }
11417
+ }
11418
+ break;
11419
+ case "CallExpression":
11420
+ // A call expression whose callee is an identifier is looked
11421
+ // up as a non-local. ie even if there's a same named local,
11422
+ // it will be ignored, and the lookup will start as if the
11423
+ // call had been written self.foo() rather than foo().
11424
+ if (node.callee.type === "Identifier") {
11425
+ if (!name || node.callee.name === name) {
11426
+ /* ignore return value */
11427
+ checkResults(state.lookupNonlocal(node.callee), node.callee);
11428
+ }
11429
+ return ["arguments"];
11430
+ }
11431
+ break;
11432
+ case "Identifier":
11433
+ if (!name || node.name === name) {
11434
+ return checkResults(state.lookup(node), node);
11435
+ }
11436
+ break;
11437
+ case "MemberExpression":
11438
+ if (!node.computed && node.property.type === "Identifier") {
11439
+ if (!name || node.property.name === name) {
11440
+ return checkResults(state.lookup(node), node) || ["object"];
11441
+ }
11442
+ return ["object"];
11443
+ }
11444
+ break;
11445
+ }
11446
+ return null;
11447
+ };
11448
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
11449
+ delete state.pre;
11450
+ }
11451
+
11363
11452
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
11364
11453
 
11365
11454
 
@@ -11367,43 +11456,24 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11367
11456
 
11368
11457
 
11369
11458
 
11370
- function processImports(allImports, lookup) {
11371
- allImports.forEach(({ node, stack }) => {
11372
- const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
11373
- if (name && module) {
11374
- const [parent] = stack.slice(-1);
11375
- if (!parent.decls)
11376
- parent.decls = {};
11377
- const decls = parent.decls;
11378
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(decls, name))
11379
- decls[name] = [];
11380
- module.forEach((m) => {
11381
- if ((0,external_api_cjs_namespaceObject.isStateNode)(m) && m.type == "ModuleDeclaration") {
11382
- (0,external_util_cjs_namespaceObject.pushUnique)(decls[name], m);
11383
- if (!parent.type_decls)
11384
- parent.type_decls = {};
11385
- const tdecls = parent.type_decls;
11386
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
11387
- tdecls[name] = [];
11388
- (0,external_util_cjs_namespaceObject.pushUnique)(tdecls[name], m);
11389
- if (node.type == "ImportModule" && m.type_decls) {
11390
- Object.entries(m.type_decls).forEach(([name, decls]) => {
11391
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
11392
- tdecls[name] = [];
11393
- decls.forEach((decl) => (0,external_util_cjs_namespaceObject.pushUnique)(tdecls[name], decl));
11394
- });
11395
- }
11396
- }
11397
- });
11398
- }
11399
- });
11400
- }
11459
+
11401
11460
  function collectClassInfo(state) {
11461
+ const toybox = state.stack[0].decls["Toybox"][0];
11462
+ const lang = toybox.decls["Lang"][0];
11463
+ const object = lang.decls["Object"];
11402
11464
  state.allClasses.forEach((elm) => {
11465
+ if (elm.stack[elm.stack.length - 1].type === "ClassDeclaration") {
11466
+ // nested classes don't get access to their contained
11467
+ // context. Put them in the global scope instead.
11468
+ elm.stack = elm.stack.slice(0, 1);
11469
+ }
11403
11470
  if (elm.node.superClass) {
11404
- const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
11405
- const superClass = classes &&
11406
- classes.filter((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && c.type === "ClassDeclaration");
11471
+ const [name, lookupDefns] = state.lookup(elm.node.superClass, null, elm.stack);
11472
+ const superClass = lookupDefns &&
11473
+ lookupDefns
11474
+ .map((lookupDefn) => lookupDefn.results)
11475
+ .flat()
11476
+ .filter((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && c.type === "ClassDeclaration");
11407
11477
  // set it "true" if there is a superClass, but we can't find it.
11408
11478
  elm.superClass = superClass && superClass.length ? superClass : true;
11409
11479
  if (name && elm.superClass !== true) {
@@ -11435,6 +11505,9 @@ function collectClassInfo(state) {
11435
11505
  elm.decls[name] = elm.superClass;
11436
11506
  }
11437
11507
  }
11508
+ else if (elm !== object[0]) {
11509
+ elm.superClass = object;
11510
+ }
11438
11511
  });
11439
11512
  const markOverrides = (cls, scls) => {
11440
11513
  if (scls === true)
@@ -11487,22 +11560,23 @@ function getFileASTs(fnMap) {
11487
11560
  return ok;
11488
11561
  }, true));
11489
11562
  }
11490
- async function analyze(fnMap) {
11563
+ async function analyze(fnMap, barrelList, config) {
11491
11564
  let hasTests = false;
11492
- const allImports = [];
11565
+ let markApi = true;
11493
11566
  const preState = {
11494
11567
  fnMap,
11568
+ config,
11495
11569
  allFunctions: [],
11496
11570
  allClasses: [],
11497
11571
  shouldExclude(node) {
11498
11572
  if ("attrs" in node &&
11499
11573
  node.attrs &&
11500
- "attrs" in node.attrs &&
11501
- node.attrs.attrs &&
11574
+ "attributes" in node.attrs &&
11575
+ node.attrs.attributes &&
11502
11576
  node.loc?.source) {
11503
11577
  const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
11504
11578
  if (excludeAnnotations) {
11505
- return node.attrs.attrs.reduce((drop, attr) => {
11579
+ return node.attrs.attributes.elements.reduce((drop, attr) => {
11506
11580
  if (attr.type != "UnaryExpression")
11507
11581
  return drop;
11508
11582
  if (attr.argument.type != "Identifier")
@@ -11519,45 +11593,37 @@ async function analyze(fnMap) {
11519
11593
  }
11520
11594
  return false;
11521
11595
  },
11522
- post(node, state) {
11596
+ pre(node, state) {
11523
11597
  switch (node.type) {
11524
11598
  case "FunctionDeclaration":
11599
+ if (markApi) {
11600
+ node.body = null;
11601
+ break;
11602
+ }
11603
+ case "ModuleDeclaration":
11525
11604
  case "ClassDeclaration": {
11526
11605
  const [scope] = state.stack.slice(-1);
11527
- const stack = state.stack.slice(0, -1);
11528
- scope.stack = stack;
11606
+ scope.stack = state.stackClone().slice(0, -1);
11529
11607
  if (scope.type == "FunctionDeclaration") {
11608
+ scope.isStatic =
11609
+ scope.stack.slice(-1)[0].type !== "ClassDeclaration" ||
11610
+ (scope.node.attrs &&
11611
+ scope.node.attrs.access &&
11612
+ scope.node.attrs.access.includes("static"));
11530
11613
  state.allFunctions.push(scope);
11531
11614
  }
11532
- else {
11615
+ else if (scope.type === "ClassDeclaration") {
11533
11616
  state.allClasses.push(scope);
11534
11617
  }
11535
- return null;
11618
+ break;
11536
11619
  }
11537
- case "Using":
11538
- case "ImportModule":
11539
- allImports.push({ node, stack: state.stack.slice() });
11540
- return null;
11541
- default:
11542
- return null;
11543
11620
  }
11621
+ return null;
11544
11622
  },
11545
11623
  };
11546
- await (0,external_api_cjs_namespaceObject.getApiMapping)(preState);
11624
+ await (0,external_api_cjs_namespaceObject.getApiMapping)(preState, barrelList);
11625
+ markApi = false;
11547
11626
  const state = preState;
11548
- // Mark all functions from api.mir as "special" by
11549
- // setting their bodies to null. In api.mir, they're
11550
- // all empty, which makes it look like they're
11551
- // do-nothing functions.
11552
- const markApi = (node) => {
11553
- if (node.type == "FunctionDeclaration") {
11554
- node.node.body = null;
11555
- }
11556
- if ((0,external_api_cjs_namespaceObject.isStateNode)(node) && node.decls) {
11557
- Object.values(node.decls).forEach((v) => v.forEach(markApi));
11558
- }
11559
- };
11560
- markApi(state.stack[0]);
11561
11627
  await getFileASTs(fnMap);
11562
11628
  Object.entries(fnMap).forEach(([name, value]) => {
11563
11629
  const { ast, parserError } = value;
@@ -11570,8 +11636,28 @@ async function analyze(fnMap) {
11570
11636
  });
11571
11637
  delete state.shouldExclude;
11572
11638
  delete state.post;
11573
- processImports(allImports, state.lookup);
11574
11639
  collectClassInfo(state);
11640
+ const diagnosticType = config?.checkInvalidSymbols !== "OFF"
11641
+ ? config?.checkInvalidSymbols || "WARNING"
11642
+ : null;
11643
+ if (diagnosticType &&
11644
+ !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
11645
+ const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
11646
+ Object.entries(fnMap).forEach(([k, v]) => {
11647
+ visitReferences(state, v.ast, null, false, (node, results, error) => {
11648
+ if (!error)
11649
+ return undefined;
11650
+ const nodeStr = (0,external_api_cjs_namespaceObject.formatAst)(node);
11651
+ if (state.inType) {
11652
+ if (!checkTypes || nodeStr.match(/^Void|Null$/)) {
11653
+ return undefined;
11654
+ }
11655
+ }
11656
+ diagnostic(state, node.loc, `Undefined symbol ${nodeStr}`, diagnosticType);
11657
+ return false;
11658
+ });
11659
+ });
11660
+ }
11575
11661
  return state;
11576
11662
  }
11577
11663
  function compareLiteralLike(a, b) {
@@ -11581,11 +11667,11 @@ function compareLiteralLike(a, b) {
11581
11667
  b = b.left;
11582
11668
  return a.type === "Literal" && b.type === "Literal" && a.value === b.value;
11583
11669
  }
11584
- function getLiteralFromDecls(decls) {
11585
- if (!decls.length)
11670
+ function getLiteralFromDecls(lookupDefns) {
11671
+ if (!lookupDefns.length)
11586
11672
  return null;
11587
11673
  let result = null;
11588
- if (decls.every((d) => {
11674
+ if (lookupDefns.every((lookupDefn) => lookupDefn.results.every((d) => {
11589
11675
  if (d.type === "EnumStringMember" ||
11590
11676
  (d.type === "VariableDeclarator" && d.node.kind === "const")) {
11591
11677
  const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
@@ -11600,7 +11686,7 @@ function getLiteralFromDecls(decls) {
11600
11686
  }
11601
11687
  }
11602
11688
  return false;
11603
- })) {
11689
+ }))) {
11604
11690
  return result;
11605
11691
  }
11606
11692
  return null;
@@ -11804,14 +11890,30 @@ function evaluateFunction(func, args) {
11804
11890
  return false;
11805
11891
  }
11806
11892
  }
11807
- async function optimizeMonkeyC(fnMap) {
11893
+ function markFunctionCalled(state, func) {
11894
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, func.id.name)) {
11895
+ state.calledFunctions[func.id.name] = [func];
11896
+ return;
11897
+ }
11898
+ (0,external_util_cjs_namespaceObject.pushUnique)(state.calledFunctions[func.id.name], func);
11899
+ }
11900
+ async function optimizeMonkeyC(fnMap, barrelList, config) {
11808
11901
  const state = {
11809
- ...(await analyze(fnMap)),
11902
+ ...(await analyze(fnMap, barrelList, config)),
11810
11903
  localsStack: [{}],
11811
11904
  exposed: {},
11812
11905
  calledFunctions: {},
11813
11906
  };
11814
- const replace = (node, obj) => {
11907
+ const replace = (node, old) => {
11908
+ if (node === false || node === null)
11909
+ return node;
11910
+ const rep = state.traverse(node);
11911
+ if (rep === false || Array.isArray(rep))
11912
+ return rep;
11913
+ return { ...(rep || node), loc: old.loc, start: old.start, end: old.end };
11914
+ };
11915
+ const inPlaceReplacement = (node, obj) => {
11916
+ const { start, end, loc } = node;
11815
11917
  for (const k of Object.keys(node)) {
11816
11918
  delete node[k];
11817
11919
  }
@@ -11819,13 +11921,16 @@ async function optimizeMonkeyC(fnMap) {
11819
11921
  obj = {
11820
11922
  type: "BinaryExpression",
11821
11923
  operator: "as",
11822
- left: obj,
11924
+ left: { ...obj, start, end, loc },
11823
11925
  right: { type: "TypeSpecList", ts: [obj.enumType] },
11824
11926
  };
11825
11927
  }
11826
11928
  for (const [k, v] of Object.entries(obj)) {
11827
11929
  node[k] = v;
11828
11930
  }
11931
+ node.loc = loc;
11932
+ node.start = start;
11933
+ node.end = end;
11829
11934
  };
11830
11935
  const lookupAndReplace = (node) => {
11831
11936
  const [, objects] = state.lookup(node);
@@ -11836,7 +11941,7 @@ async function optimizeMonkeyC(fnMap) {
11836
11941
  if (!obj) {
11837
11942
  return false;
11838
11943
  }
11839
- replace(node, obj);
11944
+ inPlaceReplacement(node, obj);
11840
11945
  return true;
11841
11946
  };
11842
11947
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
@@ -11852,8 +11957,8 @@ async function optimizeMonkeyC(fnMap) {
11852
11957
  if ((0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, func.id.name))
11853
11958
  return true;
11854
11959
  if (func.attrs &&
11855
- func.attrs.attrs &&
11856
- func.attrs.attrs.some((attr) => {
11960
+ func.attrs.attributes &&
11961
+ func.attrs.attributes.elements.some((attr) => {
11857
11962
  if (attr.type != "UnaryExpression")
11858
11963
  return false;
11859
11964
  if (attr.argument.type != "Identifier")
@@ -11944,6 +12049,37 @@ async function optimizeMonkeyC(fnMap) {
11944
12049
  }
11945
12050
  return ["init"];
11946
12051
  }
12052
+ case "CatchClause":
12053
+ if (node.param) {
12054
+ state.localsStack.push({ node, map: { ...(topLocals().map || {}) } });
12055
+ const locals = topLocals();
12056
+ const map = locals.map;
12057
+ const declName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(node.param);
12058
+ const name = renameVariable(state, locals, declName);
12059
+ if (name) {
12060
+ if (node.param.type === "Identifier") {
12061
+ node.param.name = name;
12062
+ }
12063
+ else {
12064
+ node.param.left.name = name;
12065
+ }
12066
+ }
12067
+ else {
12068
+ map[declName] = true;
12069
+ }
12070
+ return ["body"];
12071
+ }
12072
+ break;
12073
+ case "BinaryExpression":
12074
+ if (node.operator === "has") {
12075
+ if (node.right.type === "UnaryExpression" &&
12076
+ node.right.operator === ":") {
12077
+ // Using `expr has :symbol` doesn't "expose"
12078
+ // symbol. So skip the right operand.
12079
+ return ["left"];
12080
+ }
12081
+ }
12082
+ break;
11947
12083
  case "UnaryExpression":
11948
12084
  if (node.operator == ":") {
11949
12085
  // If we produce a Symbol, for a given name,
@@ -12013,10 +12149,7 @@ async function optimizeMonkeyC(fnMap) {
12013
12149
  used = checkInherited(parent, node.id.name);
12014
12150
  }
12015
12151
  if (used) {
12016
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, node.id.name)) {
12017
- state.calledFunctions[node.id.name] = [];
12018
- }
12019
- state.calledFunctions[node.id.name].push(node);
12152
+ markFunctionCalled(state, node);
12020
12153
  }
12021
12154
  }
12022
12155
  }
@@ -12029,8 +12162,7 @@ async function optimizeMonkeyC(fnMap) {
12029
12162
  }
12030
12163
  const opt = optimizeNode(node);
12031
12164
  if (opt) {
12032
- replace(node, opt);
12033
- return null;
12165
+ return replace(opt, node);
12034
12166
  }
12035
12167
  switch (node.type) {
12036
12168
  case "ConditionalExpression":
@@ -12040,7 +12172,7 @@ async function optimizeMonkeyC(fnMap) {
12040
12172
  const rep = node.test.value ? node.consequent : node.alternate;
12041
12173
  if (!rep)
12042
12174
  return false;
12043
- replace(node, rep);
12175
+ return replace(rep, rep);
12044
12176
  }
12045
12177
  break;
12046
12178
  case "WhileStatement":
@@ -12055,15 +12187,11 @@ async function optimizeMonkeyC(fnMap) {
12055
12187
  break;
12056
12188
  case "ReturnStatement":
12057
12189
  if (node.argument && node.argument.type === "CallExpression") {
12058
- return optimizeCall(state, node.argument, node);
12190
+ return replace(optimizeCall(state, node.argument, node), node.argument);
12059
12191
  }
12060
12192
  break;
12061
12193
  case "CallExpression": {
12062
- const ret = optimizeCall(state, node, null);
12063
- if (ret) {
12064
- replace(node, ret);
12065
- }
12066
- break;
12194
+ return replace(optimizeCall(state, node, null), node);
12067
12195
  }
12068
12196
  case "AssignmentExpression":
12069
12197
  if (node.operator === "=" &&
@@ -12075,7 +12203,7 @@ async function optimizeMonkeyC(fnMap) {
12075
12203
  break;
12076
12204
  case "ExpressionStatement":
12077
12205
  if (node.expression.type === "CallExpression") {
12078
- return optimizeCall(state, node.expression, node);
12206
+ return replace(optimizeCall(state, node.expression, node), node.expression);
12079
12207
  }
12080
12208
  else if (node.expression.type === "AssignmentExpression") {
12081
12209
  if (node.expression.right.type === "CallExpression") {
@@ -12087,21 +12215,20 @@ async function optimizeMonkeyC(fnMap) {
12087
12215
  }
12088
12216
  if (!ok && node.expression.operator == "=") {
12089
12217
  const [, result] = state.lookup(node.expression.left);
12090
- ok = result != null;
12218
+ ok = !!result;
12091
12219
  }
12092
12220
  if (ok) {
12093
- const ret = optimizeCall(state, node.expression.right, node.expression);
12094
- if (ret && ret.type === "BlockStatement") {
12095
- const r2 = state.traverse(ret);
12096
- return r2 === false || r2 ? r2 : ret;
12097
- }
12221
+ return replace(optimizeCall(state, node.expression.right, node.expression), node.expression.right);
12098
12222
  }
12099
12223
  }
12100
12224
  }
12101
12225
  else {
12102
12226
  const ret = unused(node.expression, true);
12103
12227
  if (ret) {
12104
- return ret;
12228
+ return ret
12229
+ .map((r) => replace(r, r))
12230
+ .flat(1)
12231
+ .filter((s) => !!s);
12105
12232
  }
12106
12233
  }
12107
12234
  break;
@@ -12111,10 +12238,18 @@ async function optimizeMonkeyC(fnMap) {
12111
12238
  Object.values(fnMap).forEach((f) => {
12112
12239
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
12113
12240
  });
12241
+ state.calledFunctions = {};
12242
+ state.exposed = {};
12243
+ Object.values(fnMap).forEach((f) => {
12244
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
12245
+ });
12114
12246
  delete state.pre;
12115
12247
  delete state.post;
12116
12248
  const cleanup = (node) => {
12117
12249
  switch (node.type) {
12250
+ case "ThisExpression":
12251
+ node.text = "self";
12252
+ break;
12118
12253
  case "EnumStringBody":
12119
12254
  if (node.members.every((m) => {
12120
12255
  const name = "name" in m ? m.name : m.id.name;
@@ -12139,19 +12274,24 @@ async function optimizeMonkeyC(fnMap) {
12139
12274
  if (!node.body.members.length) {
12140
12275
  if (!node.id)
12141
12276
  return false;
12142
- if (!node.body["enumType"]) {
12277
+ if (!node.body.enumType) {
12143
12278
  throw new Error("Missing enumType on optimized enum");
12144
12279
  }
12145
- replace(node, {
12280
+ return {
12146
12281
  type: "TypedefDeclaration",
12147
12282
  id: node.id,
12148
12283
  ts: {
12149
12284
  type: "UnaryExpression",
12150
- argument: { type: "TypeSpecList", ts: [node.body.enumType] },
12285
+ argument: {
12286
+ type: "TypeSpecList",
12287
+ ts: [
12288
+ node.body.enumType,
12289
+ ],
12290
+ },
12151
12291
  prefix: true,
12152
12292
  operator: " as",
12153
12293
  },
12154
- });
12294
+ };
12155
12295
  }
12156
12296
  break;
12157
12297
  case "VariableDeclaration": {
@@ -12174,6 +12314,19 @@ async function optimizeMonkeyC(fnMap) {
12174
12314
  return false;
12175
12315
  }
12176
12316
  break;
12317
+ case "ClassDeclaration":
12318
+ case "ModuleDeclaration":
12319
+ // none of the attributes means anything on classes and
12320
+ // modules, and the new compiler complains about some
12321
+ // of them. Just drop them all.
12322
+ if (node.attrs && node.attrs.access) {
12323
+ if (node.attrs.attributes) {
12324
+ delete node.attrs.access;
12325
+ }
12326
+ else {
12327
+ delete node.attrs;
12328
+ }
12329
+ }
12177
12330
  }
12178
12331
  return null;
12179
12332
  };
@@ -12189,7 +12342,12 @@ async function optimizeMonkeyC(fnMap) {
12189
12342
  return state.diagnostics;
12190
12343
  }
12191
12344
  function optimizeCall(state, node, context) {
12192
- const [name, callees] = state.lookup(node.callee);
12345
+ const [name, results] = state.lookupNonlocal(node.callee);
12346
+ const callees = results &&
12347
+ results
12348
+ .map((r) => r.results)
12349
+ .flat()
12350
+ .filter((c) => c.type === "FunctionDeclaration");
12193
12351
  if (!callees || !callees.length) {
12194
12352
  const n = name ||
12195
12353
  ("name" in node.callee && node.callee.name) ||
@@ -12224,32 +12382,34 @@ function optimizeCall(state, node, context) {
12224
12382
  }
12225
12383
  }
12226
12384
  }
12227
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
12228
- state.calledFunctions[name] = [];
12229
- }
12230
- callees.forEach((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && (0,external_util_cjs_namespaceObject.pushUnique)(state.calledFunctions[name], c.node));
12385
+ callees.forEach((c) => markFunctionCalled(state, c.node));
12231
12386
  return null;
12232
12387
  }
12233
12388
 
12234
12389
  ;// CONCATENATED MODULE: ./src/pragma-checker.ts
12235
12390
 
12236
- function pragmaChecker(ast) {
12391
+ function pragmaChecker(ast, diagnostics) {
12237
12392
  const comments = ast.comments;
12238
12393
  if (!comments)
12239
12394
  return;
12395
+ diagnostics = diagnostics
12396
+ ?.slice()
12397
+ .sort((d1, d2) => d1.loc.start < d2.loc.start ? -1 : d1.loc.start == d2.loc.start ? 0 : 1);
12398
+ let diagIndex = 0;
12240
12399
  let index = -1;
12241
12400
  let comment;
12242
12401
  let matchers;
12243
12402
  const next = () => {
12244
12403
  while (++index < comments.length) {
12245
12404
  comment = comments[index];
12246
- let match = comment.value.match(/^\s*@match\s+(.+)/);
12405
+ let match = comment.value.match(/^\s*@(match|expect)\s+(.+)/);
12247
12406
  if (!match)
12248
12407
  continue;
12249
- let str = match[1];
12408
+ const kind = match[1];
12409
+ let str = match[2];
12250
12410
  matchers = [];
12251
12411
  while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
12252
- matchers.push({ quote: match[1], needle: match[2] });
12412
+ matchers.push({ kind, quote: match[1], needle: match[2] });
12253
12413
  str = str.substring(match[0].length);
12254
12414
  if (!str.length)
12255
12415
  break;
@@ -12259,35 +12419,79 @@ function pragmaChecker(ast) {
12259
12419
  if (!matchers.length) {
12260
12420
  match = str.match(/^(\S+)\s+$/);
12261
12421
  if (match) {
12262
- matchers.push({ quote: '"', needle: match[1] });
12422
+ matchers.push({ kind, quote: '"', needle: match[1] });
12263
12423
  break;
12264
12424
  }
12265
12425
  }
12266
12426
  throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12267
12427
  }
12268
12428
  };
12429
+ const matcher = (quote, needle, haystack) => {
12430
+ if (quote == '"') {
12431
+ return haystack.includes(needle);
12432
+ }
12433
+ const re = new RegExp(needle);
12434
+ return re.test(haystack);
12435
+ };
12269
12436
  next();
12270
12437
  (0,external_api_cjs_namespaceObject.traverseAst)(ast, (node) => {
12271
12438
  if (index >= comments.length)
12272
12439
  return false;
12273
12440
  if (node.start && node.start >= (comment.end || Infinity)) {
12274
- const { quote, needle } = matchers.shift();
12275
- const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/[\r\n]/g, " ");
12276
- let found = false;
12277
- if (quote == '"') {
12278
- found = haystack.includes(needle);
12279
- }
12280
- else {
12281
- const re = new RegExp(needle);
12282
- found = re.test(haystack);
12441
+ const { kind, quote, needle } = matchers.shift();
12442
+ if (kind === "match") {
12443
+ if (!matcher(quote, needle, (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/([\r\n]|\s)+/g, " "))) {
12444
+ throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
12445
+ }
12283
12446
  }
12284
- if (!found) {
12285
- throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
12447
+ else if (kind === "expect") {
12448
+ const locCmp = (a, b) => {
12449
+ if (!b)
12450
+ return -1;
12451
+ if (a.start.line < b.start.line)
12452
+ return -1;
12453
+ if (a.start.line === b.start.line &&
12454
+ a.start.column < b.start.column) {
12455
+ return -1;
12456
+ }
12457
+ if (a.end.line > b.end.line)
12458
+ return 1;
12459
+ if (a.end.line === b.end.line && a.end.column >= b.end.column) {
12460
+ return 1;
12461
+ }
12462
+ return 0;
12463
+ };
12464
+ let found = false;
12465
+ if (diagnostics) {
12466
+ while (true) {
12467
+ if (diagIndex >= diagnostics.length) {
12468
+ diagnostics = null;
12469
+ break;
12470
+ }
12471
+ const diag = diagnostics[diagIndex];
12472
+ const cmp = locCmp(diag.loc, node.loc);
12473
+ if (cmp > 0) {
12474
+ break;
12475
+ }
12476
+ diagIndex++;
12477
+ if (cmp < 0)
12478
+ continue;
12479
+ if (matcher(quote, needle, diag.message)) {
12480
+ found = true;
12481
+ diag.type = "INFO";
12482
+ }
12483
+ }
12484
+ }
12485
+ if (!found) {
12486
+ throw new Error(`Missing error message '${needle} at ${comment.loc.source}:${comment.loc.start.line}`);
12487
+ }
12286
12488
  }
12287
- if (!matchers.length) {
12288
- next();
12489
+ if (matchers.length) {
12490
+ // if we're checking a series of nodes, we need
12491
+ // to skip over this one.
12492
+ return false;
12289
12493
  }
12290
- return false;
12494
+ next();
12291
12495
  }
12292
12496
  return null;
12293
12497
  });
@@ -12675,6 +12879,7 @@ const configOptionsToCheck = [
12675
12879
  "ignoredExcludeAnnotations",
12676
12880
  "ignoredAnnotations",
12677
12881
  "ignoredSourcePaths",
12882
+ "checkInvalidSymbols",
12678
12883
  ];
12679
12884
  /**
12680
12885
  * @param {BuildConfig} config
@@ -12726,21 +12931,21 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12726
12931
  // the oldest optimized file, we don't need to regenerate
12727
12932
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12728
12933
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12729
- if (source_time < opt_time && 1654554780017 < opt_time) {
12934
+ if (source_time < opt_time && 1655479110007 < opt_time) {
12730
12935
  return { hasTests, diagnostics: prevDiagnostics };
12731
12936
  }
12732
12937
  }
12733
12938
  await promises_namespaceObject.rm(output, { recursive: true, force: true });
12734
12939
  await promises_namespaceObject.mkdir(output, { recursive: true });
12735
- const diagnostics = await optimizeMonkeyC(fnMap);
12736
- return Promise.all(Object.values(fnMap).map(async (info) => {
12940
+ const diagnostics = await optimizeMonkeyC(fnMap, Object.keys(buildConfig.barrelMap || {}), config);
12941
+ return Promise.all(Object.entries(fnMap).map(async ([inFile, info]) => {
12737
12942
  const name = info.output;
12738
12943
  const dir = external_path_.dirname(name);
12739
12944
  await promises_namespaceObject.mkdir(dir, { recursive: true });
12740
12945
  const opt_source = (0,external_api_cjs_namespaceObject.formatAst)(info.ast, info.monkeyCSource);
12741
12946
  await promises_namespaceObject.writeFile(name, opt_source);
12742
12947
  if (config.checkBuildPragmas) {
12743
- pragmaChecker(info.ast);
12948
+ pragmaChecker(info.ast, diagnostics?.[inFile]);
12744
12949
  }
12745
12950
  return info.hasTests;
12746
12951
  })).then((results) => {
@@ -12775,7 +12980,13 @@ async function getProjectAnalysis(targets, analysis, options) {
12775
12980
  if (!(await getFileASTs(fnMap))) {
12776
12981
  return { fnMap, paths };
12777
12982
  }
12778
- const state = await analyze(fnMap);
12983
+ const barrelObj = {};
12984
+ targets.forEach((target) => {
12985
+ if (target.qualifier.barrelMap) {
12986
+ Object.keys(target.qualifier.barrelMap).forEach((key) => (barrelObj[key] = true));
12987
+ }
12988
+ });
12989
+ const state = await analyze(fnMap, Object.keys(barrelObj), options);
12779
12990
  return { fnMap: fnMap, paths, state };
12780
12991
  }
12781
12992
  /**