@markw65/monkeyc-optimizer 1.0.17 → 1.0.20

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
@@ -10902,7 +10902,7 @@ function getArgSafety(state, func, args, requireAll) {
10902
10902
  })) {
10903
10903
  return false;
10904
10904
  }
10905
- if (allSafe)
10905
+ if (allSafe && requireAll)
10906
10906
  return true;
10907
10907
  let callSeen = false;
10908
10908
  let ok = true;
@@ -10925,10 +10925,16 @@ function getArgSafety(state, func, args, requireAll) {
10925
10925
  .map(([key]) => key);
10926
10926
  }, (node) => {
10927
10927
  switch (node.type) {
10928
+ case "AssignmentExpression":
10929
+ case "UpdateExpression": {
10930
+ const v = node.type == "UpdateExpression" ? node.argument : node.left;
10931
+ if (v.type === "Identifier" && (0,external_api_cjs_namespaceObject.hasProperty)(params, v.name)) {
10932
+ safeArgs[params[v.name]] = null;
10933
+ }
10934
+ }
10935
+ // fall through
10928
10936
  case "CallExpression":
10929
10937
  case "NewExpression":
10930
- case "AssignmentExpression":
10931
- case "UpdateExpression":
10932
10938
  callSeen = true;
10933
10939
  break;
10934
10940
  case "Identifier":
@@ -10977,9 +10983,24 @@ var InlineStatus;
10977
10983
  InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
10978
10984
  InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
10979
10985
  })(InlineStatus || (InlineStatus = {}));
10980
- function shouldInline(state, func, args) {
10986
+ function inlineRequested(state, func) {
10987
+ const excludeAnnotations = (func.node.loc?.source &&
10988
+ state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
10989
+ {};
10990
+ if (func.node.attrs &&
10991
+ func.node.attrs.attrs &&
10992
+ func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
10993
+ (attr.argument.name === "inline" ||
10994
+ (attr.argument.name.startsWith("inline_") &&
10995
+ (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))))) {
10996
+ return true;
10997
+ }
10998
+ return false;
10999
+ }
11000
+ function shouldInline(state, func, call, context) {
10981
11001
  let autoInline = false;
10982
11002
  let inlineAsExpression = false;
11003
+ const args = call.arguments;
10983
11004
  if (func.node.body &&
10984
11005
  func.node.body.body.length === 1 &&
10985
11006
  func.node.body.body[0].type === "ReturnStatement" &&
@@ -10989,39 +11010,30 @@ function shouldInline(state, func, args) {
10989
11010
  autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
10990
11011
  }
10991
11012
  if (autoInline === 1) {
10992
- return InlineStatus.AsExpression;
11013
+ return true;
10993
11014
  }
10994
- const excludeAnnotations = (func.node.loc?.source &&
10995
- state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
10996
- {};
10997
- const inlineRequested = func.node.attrs &&
10998
- func.node.attrs.attrs &&
10999
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
11000
- (attr.argument.name === "inline" ||
11001
- (attr.argument.name.startsWith("inline_") &&
11002
- (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))));
11003
- if (autoInline || inlineRequested) {
11004
- return inlineAsExpression && canInline(state, func, args)
11005
- ? InlineStatus.AsExpression
11006
- : InlineStatus.AsStatement;
11015
+ const requested = inlineRequested(state, func);
11016
+ if (autoInline || requested) {
11017
+ if (inlineAsExpression) {
11018
+ if (canInline(state, func, args)) {
11019
+ return true;
11020
+ }
11021
+ }
11022
+ if (!context && requested) {
11023
+ inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, or return contexts");
11024
+ }
11025
+ return context != null;
11007
11026
  }
11008
- return InlineStatus.Never;
11027
+ return false;
11009
11028
  }
11010
11029
  function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
11011
- if (!params) {
11012
- const safeArgs = getArgSafety(state, func, call.arguments, false);
11013
- params = Object.fromEntries(func.node.params.map((param, i) => {
11014
- const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
11015
- ? i
11016
- : -1;
11017
- const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11018
- return [name, argnum];
11019
- }));
11020
- }
11030
+ let failed = false;
11021
11031
  const pre = state.pre;
11022
11032
  const post = state.post;
11023
11033
  try {
11024
11034
  state.pre = (node) => {
11035
+ if (failed)
11036
+ return [];
11025
11037
  node.start = call.start;
11026
11038
  node.end = call.end;
11027
11039
  node.loc = call.loc;
@@ -11033,6 +11045,11 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11033
11045
  const { map } = locals;
11034
11046
  if (!map)
11035
11047
  throw new Error("No local variable map!");
11048
+ // We still need to keep track of every local name that was
11049
+ // already in use, but we don't want to use any of its renames.
11050
+ // We also want to know whether a local is from the function being
11051
+ // inlined, or the calling function, so set every element to false.
11052
+ Object.keys(map).forEach((key) => (map[key] = false));
11036
11053
  const declarations = func.node.params
11037
11054
  .map((param, i) => {
11038
11055
  const paramName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
@@ -11057,6 +11074,8 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11057
11074
  return result;
11058
11075
  };
11059
11076
  state.post = (node) => {
11077
+ if (failed)
11078
+ return null;
11060
11079
  let replacement = null;
11061
11080
  switch (node.type) {
11062
11081
  case "Identifier": {
@@ -11071,22 +11090,28 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11071
11090
  }
11072
11091
  replacement = fixNodeScope(state, node, func.stack);
11073
11092
  if (!replacement) {
11074
- throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
11093
+ failed = true;
11094
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11095
+ return null;
11075
11096
  }
11076
11097
  break;
11077
11098
  }
11078
11099
  }
11079
11100
  return post(replacement || node, state) || replacement;
11080
11101
  };
11081
- return state.traverse(root) || null;
11082
- }
11083
- catch (ex) {
11084
- if (ex instanceof Error) {
11085
- if (ex.message.startsWith("Inliner: ")) {
11086
- return null;
11087
- }
11102
+ let ret = state.traverse(root);
11103
+ if (failed) {
11104
+ return null;
11105
+ }
11106
+ if (ret === null) {
11107
+ ret = root;
11088
11108
  }
11089
- throw ex;
11109
+ if (!ret) {
11110
+ inlineDiagnostic(state, func, call, `Internal error`);
11111
+ return null;
11112
+ }
11113
+ inlineDiagnostic(state, func, call, null);
11114
+ return ret;
11090
11115
  }
11091
11116
  finally {
11092
11117
  state.pre = pre;
@@ -11109,7 +11134,16 @@ function unused(expression, top) {
11109
11134
  case "UnaryExpression":
11110
11135
  return unused(expression.argument);
11111
11136
  case "MemberExpression":
11112
- return unused(expression.object).concat(unused(expression.property));
11137
+ if (expression.computed) {
11138
+ return unused(expression.object).concat(unused(expression.property));
11139
+ }
11140
+ return unused(expression.object);
11141
+ case "ArrayExpression":
11142
+ return expression.elements.map((e) => unused(e)).flat(1);
11143
+ case "ObjectExpression":
11144
+ return expression.properties
11145
+ .map((p) => unused(p.key).concat(unused(p.value)))
11146
+ .flat(1);
11113
11147
  }
11114
11148
  return top
11115
11149
  ? null
@@ -11117,30 +11151,98 @@ function unused(expression, top) {
11117
11151
  {
11118
11152
  type: "ExpressionStatement",
11119
11153
  expression,
11154
+ start: expression.start,
11155
+ end: expression.end,
11156
+ loc: expression.loc,
11120
11157
  },
11121
11158
  ];
11122
11159
  }
11123
- function inlineWithArgs(state, func, call) {
11160
+ function diagnostic(state, loc, message) {
11161
+ if (!loc || !loc.source)
11162
+ return;
11163
+ const source = loc.source;
11164
+ if (!state.diagnostics)
11165
+ state.diagnostics = {};
11166
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
11167
+ if (!message)
11168
+ return;
11169
+ state.diagnostics[source] = [];
11170
+ }
11171
+ const diags = state.diagnostics[source];
11172
+ let index = diags.findIndex((item) => item.loc === loc);
11173
+ if (message) {
11174
+ if (index < 0)
11175
+ index = diags.length;
11176
+ diags[index] = { type: "INFO", loc, message };
11177
+ }
11178
+ else if (index >= 0) {
11179
+ diags.splice(index, 1);
11180
+ }
11181
+ }
11182
+ function inlineDiagnostic(state, func, call, message) {
11183
+ if (inlineRequested(state, func)) {
11184
+ diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
11185
+ }
11186
+ }
11187
+ function inlineWithArgs(state, func, call, context) {
11124
11188
  if (!func.node || !func.node.body) {
11125
11189
  return null;
11126
11190
  }
11127
11191
  let retStmtCount = 0;
11128
- (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
11129
- node.type === "ReturnStatement" && retStmtCount++;
11130
- });
11131
- if (retStmtCount > 1 ||
11132
- (retStmtCount === 1 &&
11133
- func.node.body.body.slice(-1)[0].type !== "ReturnStatement")) {
11134
- return null;
11192
+ if (context.type === "ReturnStatement") {
11193
+ const last = func.node.body.body.slice(-1)[0];
11194
+ if (!last || last.type !== "ReturnStatement") {
11195
+ inlineDiagnostic(state, func, call, "Function didn't end with a return statement");
11196
+ return null;
11197
+ }
11198
+ }
11199
+ else {
11200
+ (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
11201
+ node.type === "ReturnStatement" && retStmtCount++;
11202
+ });
11203
+ if (retStmtCount > 1) {
11204
+ inlineDiagnostic(state, func, call, "Function had more than one return statement");
11205
+ }
11206
+ else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
11207
+ inlineDiagnostic(state, func, call, "Function did not have a return statement");
11208
+ return null;
11209
+ }
11210
+ if (retStmtCount === 1) {
11211
+ const last = func.node.body.body.slice(-1)[0];
11212
+ if (!last ||
11213
+ last.type !== "ReturnStatement" ||
11214
+ (context.type === "AssignmentExpression" && !last.argument)) {
11215
+ inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
11216
+ return null;
11217
+ }
11218
+ }
11135
11219
  }
11136
11220
  const body = JSON.parse(JSON.stringify(func.node.body));
11137
- processInlineBody(state, func, call, body, func.node.params.length ? false : true);
11138
- if (retStmtCount) {
11221
+ const safeArgs = getArgSafety(state, func, call.arguments, false);
11222
+ const params = Object.fromEntries(func.node.params.map((param, i) => {
11223
+ const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
11224
+ ? i
11225
+ : -1;
11226
+ const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11227
+ return [name, argnum];
11228
+ }));
11229
+ if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
11230
+ return null;
11231
+ }
11232
+ diagnostic(state, call.loc, null);
11233
+ if (context.type !== "ReturnStatement" && retStmtCount) {
11139
11234
  const last = body.body[body.body.length - 1];
11140
11235
  if (last.type != "ReturnStatement") {
11141
11236
  throw new Error("ReturnStatement got lost!");
11142
11237
  }
11143
- if (last.argument) {
11238
+ if (context.type === "AssignmentExpression") {
11239
+ context.right = last.argument;
11240
+ body.body[body.body.length - 1] = {
11241
+ type: "ExpressionStatement",
11242
+ expression: context,
11243
+ };
11244
+ }
11245
+ else if (last.argument) {
11144
11246
  const side_exprs = unused(last.argument);
11145
11247
  body.body.splice(body.body.length - 1, 1, ...side_exprs);
11146
11248
  }
@@ -11150,13 +11252,13 @@ function inlineWithArgs(state, func, call) {
11150
11252
  }
11151
11253
  return body;
11152
11254
  }
11153
- function inlineFunction(state, func, call, inlineStatus) {
11154
- if (inlineStatus == InlineStatus.AsStatement) {
11155
- return inlineWithArgs(state, func, call);
11255
+ function inlineFunction(state, func, call, context) {
11256
+ if (context) {
11257
+ return inlineWithArgs(state, func, call, context);
11156
11258
  }
11157
11259
  const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
11158
11260
  const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
11159
- return processInlineBody(state, func, call, retArg, true, params) || retArg;
11261
+ return processInlineBody(state, func, call, retArg, true, params);
11160
11262
  }
11161
11263
  function applyTypeIfNeeded(node) {
11162
11264
  if ("enumType" in node && node.enumType) {
@@ -11171,13 +11273,14 @@ function applyTypeIfNeeded(node) {
11171
11273
  }
11172
11274
  function fixNodeScope(state, lookupNode, nodeStack) {
11173
11275
  if (lookupNode.type === "Identifier") {
11174
- for (let i = state.stack.length; --i > nodeStack.length;) {
11175
- const si = state.stack[i];
11176
- if ((0,external_api_cjs_namespaceObject.hasProperty)(si.decls, lookupNode.name)) {
11177
- // its a local from the inlined function.
11178
- // Nothing to do.
11179
- return lookupNode;
11180
- }
11276
+ const locals = state.localsStack[state.localsStack.length - 1];
11277
+ const { map } = locals;
11278
+ if (!map)
11279
+ throw new Error("No local variable map!");
11280
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(map, lookupNode.name) && map[lookupNode.name] !== false) {
11281
+ // map[name] !== false means its an entry that was created during inlining
11282
+ // so its definitely one of our locals.
11283
+ return lookupNode;
11181
11284
  }
11182
11285
  }
11183
11286
  const [, original] = state.lookup(lookupNode, null, nodeStack);
@@ -11650,13 +11753,15 @@ function optimizeNode(node) {
11650
11753
  return null;
11651
11754
  }
11652
11755
  function evaluateFunction(func, args) {
11653
- if (args && args.length != func.params.length) {
11756
+ if (!func.body || (args && args.length != func.params.length)) {
11654
11757
  return false;
11655
11758
  }
11656
11759
  const paramValues = args &&
11657
11760
  Object.fromEntries(func.params.map((p, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(p), args[i]]));
11658
11761
  let ret = null;
11659
- const body = args ? JSON.parse(JSON.stringify(func.body)) : func.body;
11762
+ const body = args
11763
+ ? JSON.parse(JSON.stringify(func.body))
11764
+ : func.body;
11660
11765
  try {
11661
11766
  (0,external_api_cjs_namespaceObject.traverseAst)(body, (node) => {
11662
11767
  switch (node.type) {
@@ -11699,6 +11804,13 @@ function evaluateFunction(func, args) {
11699
11804
  return false;
11700
11805
  }
11701
11806
  }
11807
+ function markFunctionCalled(state, func) {
11808
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, func.id.name)) {
11809
+ state.calledFunctions[func.id.name] = [func];
11810
+ return;
11811
+ }
11812
+ (0,external_util_cjs_namespaceObject.pushUnique)(state.calledFunctions[func.id.name], func);
11813
+ }
11702
11814
  async function optimizeMonkeyC(fnMap) {
11703
11815
  const state = {
11704
11816
  ...(await analyze(fnMap)),
@@ -11857,7 +11969,7 @@ async function optimizeMonkeyC(fnMap) {
11857
11969
  if (map) {
11858
11970
  if ((0,external_api_cjs_namespaceObject.hasProperty)(map, node.name)) {
11859
11971
  const name = map[node.name];
11860
- if (name !== true) {
11972
+ if (typeof name === "string") {
11861
11973
  node.name = name;
11862
11974
  }
11863
11975
  }
@@ -11908,10 +12020,7 @@ async function optimizeMonkeyC(fnMap) {
11908
12020
  used = checkInherited(parent, node.id.name);
11909
12021
  }
11910
12022
  if (used) {
11911
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, node.id.name)) {
11912
- state.calledFunctions[node.id.name] = [];
11913
- }
11914
- state.calledFunctions[node.id.name].push(node);
12023
+ markFunctionCalled(state, node);
11915
12024
  }
11916
12025
  }
11917
12026
  }
@@ -11948,27 +12057,61 @@ async function optimizeMonkeyC(fnMap) {
11948
12057
  return node.body;
11949
12058
  }
11950
12059
  break;
12060
+ case "ReturnStatement":
12061
+ if (node.argument && node.argument.type === "CallExpression") {
12062
+ return optimizeCall(state, node.argument, node);
12063
+ }
12064
+ break;
11951
12065
  case "CallExpression": {
11952
- const ret = optimizeCall(state, node, false);
12066
+ const ret = optimizeCall(state, node, null);
11953
12067
  if (ret) {
11954
12068
  replace(node, ret);
11955
12069
  }
11956
12070
  break;
11957
12071
  }
12072
+ case "AssignmentExpression":
12073
+ if (node.operator === "=" &&
12074
+ node.left.type === "Identifier" &&
12075
+ node.right.type === "Identifier" &&
12076
+ node.left.name === node.right.name) {
12077
+ return { type: "Literal", value: null, raw: "null" };
12078
+ }
12079
+ break;
11958
12080
  case "ExpressionStatement":
11959
12081
  if (node.expression.type === "CallExpression") {
11960
- const ret = optimizeCall(state, node.expression, true);
11961
- if (ret) {
11962
- if (ret.type === "BlockStatement") {
11963
- return ret;
12082
+ return optimizeCall(state, node.expression, node);
12083
+ }
12084
+ else if (node.expression.type === "AssignmentExpression") {
12085
+ if (node.expression.right.type === "CallExpression") {
12086
+ let ok = false;
12087
+ if (node.expression.left.type === "Identifier") {
12088
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(topLocals().map, node.expression.left.type)) {
12089
+ ok = true;
12090
+ }
12091
+ }
12092
+ if (!ok && node.expression.operator == "=") {
12093
+ const [, result] = state.lookup(node.expression.left);
12094
+ ok = result != null;
12095
+ }
12096
+ if (ok) {
12097
+ const ret = optimizeCall(state, node.expression.right, node.expression);
12098
+ if (ret && ret.type === "BlockStatement") {
12099
+ const r2 = state.traverse(ret);
12100
+ return r2 === false || r2 ? r2 : ret;
12101
+ }
11964
12102
  }
11965
- node.expression = ret;
11966
12103
  }
11967
12104
  }
11968
12105
  else {
11969
12106
  const ret = unused(node.expression, true);
11970
12107
  if (ret) {
11971
- return ret;
12108
+ return ret
12109
+ .map((s) => {
12110
+ const r2 = state.traverse(s);
12111
+ return r2 === false || r2 ? r2 : s;
12112
+ })
12113
+ .flat(1)
12114
+ .filter((s) => s !== false);
11972
12115
  }
11973
12116
  }
11974
12117
  break;
@@ -11978,6 +12121,11 @@ async function optimizeMonkeyC(fnMap) {
11978
12121
  Object.values(fnMap).forEach((f) => {
11979
12122
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
11980
12123
  });
12124
+ state.calledFunctions = {};
12125
+ state.exposed = {};
12126
+ Object.values(fnMap).forEach((f) => {
12127
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
12128
+ });
11981
12129
  delete state.pre;
11982
12130
  delete state.post;
11983
12131
  const cleanup = (node) => {
@@ -12053,9 +12201,12 @@ async function optimizeMonkeyC(fnMap) {
12053
12201
  return ret;
12054
12202
  });
12055
12203
  });
12204
+ return state.diagnostics;
12056
12205
  }
12057
- function optimizeCall(state, node, asStatement) {
12058
- const [name, callees] = state.lookup(node.callee);
12206
+ function optimizeCall(state, node, context) {
12207
+ const [name, results] = state.lookup(node.callee);
12208
+ const callees = results &&
12209
+ results.filter((c) => c.type === "FunctionDeclaration");
12059
12210
  if (!callees || !callees.length) {
12060
12211
  const n = name ||
12061
12212
  ("name" in node.callee && node.callee.name) ||
@@ -12074,7 +12225,8 @@ function optimizeCall(state, node, asStatement) {
12074
12225
  }
12075
12226
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
12076
12227
  const callee = callees[0].node;
12077
- if (callee.optimizable &&
12228
+ if (!context &&
12229
+ callee.optimizable &&
12078
12230
  !callee.hasOverride &&
12079
12231
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12080
12232
  const ret = evaluateFunction(callee, node.arguments);
@@ -12082,19 +12234,14 @@ function optimizeCall(state, node, asStatement) {
12082
12234
  return ret;
12083
12235
  }
12084
12236
  }
12085
- const inlineStatus = shouldInline(state, callees[0], node.arguments);
12086
- if (inlineStatus === InlineStatus.AsExpression ||
12087
- (asStatement && inlineStatus === InlineStatus.AsStatement)) {
12088
- const ret = inlineFunction(state, callees[0], node, inlineStatus);
12237
+ if (shouldInline(state, callees[0], node, context)) {
12238
+ const ret = inlineFunction(state, callees[0], node, context);
12089
12239
  if (ret) {
12090
12240
  return ret;
12091
12241
  }
12092
12242
  }
12093
12243
  }
12094
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
12095
- state.calledFunctions[name] = [];
12096
- }
12097
- callees.forEach((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && state.calledFunctions[name].push(c.node));
12244
+ callees.forEach((c) => markFunctionCalled(state, c.node));
12098
12245
  return null;
12099
12246
  }
12100
12247
 
@@ -12106,12 +12253,31 @@ function pragmaChecker(ast) {
12106
12253
  return;
12107
12254
  let index = -1;
12108
12255
  let comment;
12256
+ let matchers;
12109
12257
  const next = () => {
12110
12258
  while (++index < comments.length) {
12111
12259
  comment = comments[index];
12112
- if (comment.value.match(/^\s*@match\s+/)) {
12260
+ let match = comment.value.match(/^\s*@match\s+(.+)/);
12261
+ if (!match)
12262
+ continue;
12263
+ let str = match[1];
12264
+ matchers = [];
12265
+ while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
12266
+ matchers.push({ quote: match[1], needle: match[2] });
12267
+ str = str.substring(match[0].length);
12268
+ if (!str.length)
12269
+ break;
12270
+ }
12271
+ if (!str.length)
12113
12272
  break;
12273
+ if (!matchers.length) {
12274
+ match = str.match(/^(\S+)\s+$/);
12275
+ if (match) {
12276
+ matchers.push({ quote: '"', needle: match[1] });
12277
+ break;
12278
+ }
12114
12279
  }
12280
+ throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12115
12281
  }
12116
12282
  };
12117
12283
  next();
@@ -12119,29 +12285,23 @@ function pragmaChecker(ast) {
12119
12285
  if (index >= comments.length)
12120
12286
  return false;
12121
12287
  if (node.start && node.start >= (comment.end || Infinity)) {
12122
- let match = comment.value.match(/^\s*@match\s+([/%&#@"])(.+(?<!\\)(?:\\{2})*)\1\s+$/) || comment.value.match(/^\s*@match\s+(\S+)\s+$/);
12123
- if (!match) {
12124
- throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12125
- }
12288
+ const { quote, needle } = matchers.shift();
12126
12289
  const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/[\r\n]/g, " ");
12127
12290
  let found = false;
12128
- let needle = match[1];
12129
- if (match.length == 2) {
12291
+ if (quote == '"') {
12130
12292
  found = haystack.includes(needle);
12131
12293
  }
12132
12294
  else {
12133
- if (needle == '"') {
12134
- found = haystack.includes((needle = match[2]));
12135
- }
12136
- else {
12137
- const re = new RegExp((needle = match[2]));
12138
- found = re.test(haystack);
12139
- }
12295
+ const re = new RegExp(needle);
12296
+ found = re.test(haystack);
12140
12297
  }
12141
12298
  if (!found) {
12142
- throw new Error(`Didn't find '${needle}' in ${comment.loc.source}:${comment.loc.start.line}`);
12299
+ throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
12300
+ }
12301
+ if (!matchers.length) {
12302
+ next();
12143
12303
  }
12144
- next();
12304
+ return false;
12145
12305
  }
12146
12306
  return null;
12147
12307
  });
@@ -12230,7 +12390,7 @@ async function buildOptimizedProject(product, options) {
12230
12390
  config.releaseBuild = true;
12231
12391
  }
12232
12392
  }
12233
- const { jungleFiles, program, hasTests } = await generateOptimizedProject(config);
12393
+ const { jungleFiles, program, hasTests, diagnostics } = await generateOptimizedProject(config);
12234
12394
  config.jungleFiles = jungleFiles;
12235
12395
  let bin = config.buildDir || "bin";
12236
12396
  let name = `optimized-${program}.prg`;
@@ -12249,6 +12409,7 @@ async function buildOptimizedProject(product, options) {
12249
12409
  delete config.testBuild;
12250
12410
  return build_project(product, config).then((result) => ({
12251
12411
  hasTests,
12412
+ diagnostics,
12252
12413
  ...result,
12253
12414
  }));
12254
12415
  }
@@ -12387,6 +12548,7 @@ async function generateOptimizedProject(options) {
12387
12548
  (await checkManifest(xml, targets.map((t) => t.product)))) &&
12388
12549
  !dropBarrels;
12389
12550
  let hasTests = false;
12551
+ let diagnostics = {};
12390
12552
  const promises = Object.keys(buildConfigs)
12391
12553
  .sort()
12392
12554
  .map((key) => {
@@ -12404,7 +12566,13 @@ async function generateOptimizedProject(options) {
12404
12566
  e.products = products[key];
12405
12567
  throw e;
12406
12568
  })
12407
- .then((t) => t && (hasTests = true))
12569
+ .then((t) => {
12570
+ if (t.hasTests)
12571
+ hasTests = true;
12572
+ if (t.diagnostics) {
12573
+ diagnostics = { ...diagnostics, ...t.diagnostics };
12574
+ }
12575
+ })
12408
12576
  : promises_namespaceObject.rm(external_path_.resolve(workspace, outputPath), {
12409
12577
  recursive: true,
12410
12578
  force: true,
@@ -12475,6 +12643,7 @@ async function generateOptimizedProject(options) {
12475
12643
  xml,
12476
12644
  program: external_path_.basename(external_path_.dirname(manifest)),
12477
12645
  hasTests,
12646
+ diagnostics,
12478
12647
  };
12479
12648
  }
12480
12649
  async function fileInfoFromConfig(workspace, output, buildConfig, extraExcludes) {
@@ -12553,7 +12722,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12553
12722
  const actualOptimizedFiles = (await (0,external_util_cjs_namespaceObject.globa)(external_path_.join(output, "**", "*.mc"), { mark: true }))
12554
12723
  .filter((file) => !file.endsWith("/"))
12555
12724
  .sort();
12556
- const { hasTests, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12725
+ const { hasTests, diagnostics: prevDiagnostics, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12557
12726
  .catch(() => "{}"));
12558
12727
  // check that the set of files thats actually there is the same as the
12559
12728
  // set of files we're going to generate (in case eg a jungle file change
@@ -12571,13 +12740,13 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12571
12740
  // the oldest optimized file, we don't need to regenerate
12572
12741
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12573
12742
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12574
- if (source_time < opt_time && 1654384985724 < opt_time) {
12575
- return hasTests;
12743
+ if (source_time < opt_time && 1654708837029 < opt_time) {
12744
+ return { hasTests, diagnostics: prevDiagnostics };
12576
12745
  }
12577
12746
  }
12578
12747
  await promises_namespaceObject.rm(output, { recursive: true, force: true });
12579
12748
  await promises_namespaceObject.mkdir(output, { recursive: true });
12580
- await optimizeMonkeyC(fnMap);
12749
+ const diagnostics = await optimizeMonkeyC(fnMap);
12581
12750
  return Promise.all(Object.values(fnMap).map(async (info) => {
12582
12751
  const name = info.output;
12583
12752
  const dir = external_path_.dirname(name);
@@ -12592,9 +12761,10 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12592
12761
  const hasTests = results.some((v) => v);
12593
12762
  return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
12594
12763
  hasTests,
12764
+ diagnostics,
12595
12765
  ...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
12596
12766
  }))
12597
- .then(() => hasTests);
12767
+ .then(() => ({ hasTests, diagnostics }));
12598
12768
  });
12599
12769
  }
12600
12770
  async function getProjectAnalysis(targets, analysis, options) {