@markw65/monkeyc-optimizer 1.0.18 → 1.0.21

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;
@@ -10983,9 +10992,26 @@ var InlineStatus;
10983
10992
  InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
10984
10993
  InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
10985
10994
  })(InlineStatus || (InlineStatus = {}));
10986
- function shouldInline(state, func, args) {
10995
+ function inlineRequested(state, func) {
10996
+ const excludeAnnotations = (func.node.loc?.source &&
10997
+ state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
10998
+ {};
10999
+ if (func.node.attrs &&
11000
+ func.node.attrs.attributes &&
11001
+ func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
11002
+ (attr.argument.name === "inline" ||
11003
+ (attr.argument.name.startsWith("inline_") &&
11004
+ (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))))) {
11005
+ return true;
11006
+ }
11007
+ return false;
11008
+ }
11009
+ function shouldInline(state, func, call, context) {
11010
+ if (state.inlining)
11011
+ return false;
10987
11012
  let autoInline = false;
10988
11013
  let inlineAsExpression = false;
11014
+ const args = call.arguments;
10989
11015
  if (func.node.body &&
10990
11016
  func.node.body.body.length === 1 &&
10991
11017
  func.node.body.body[0].type === "ReturnStatement" &&
@@ -10995,39 +11021,31 @@ function shouldInline(state, func, args) {
10995
11021
  autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
10996
11022
  }
10997
11023
  if (autoInline === 1) {
10998
- return InlineStatus.AsExpression;
11024
+ return true;
10999
11025
  }
11000
- const excludeAnnotations = (func.node.loc?.source &&
11001
- state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
11002
- {};
11003
- const inlineRequested = func.node.attrs &&
11004
- func.node.attrs.attrs &&
11005
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
11006
- (attr.argument.name === "inline" ||
11007
- (attr.argument.name.startsWith("inline_") &&
11008
- (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7)))));
11009
- if (autoInline || inlineRequested) {
11010
- return inlineAsExpression && canInline(state, func, args)
11011
- ? InlineStatus.AsExpression
11012
- : InlineStatus.AsStatement;
11026
+ const requested = inlineRequested(state, func);
11027
+ if (autoInline || requested) {
11028
+ if (inlineAsExpression) {
11029
+ if (canInline(state, func, args)) {
11030
+ return true;
11031
+ }
11032
+ }
11033
+ if (!context && requested) {
11034
+ inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, or return contexts");
11035
+ }
11036
+ return context != null;
11013
11037
  }
11014
- return InlineStatus.Never;
11038
+ return false;
11015
11039
  }
11016
11040
  function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
11017
- if (!params) {
11018
- const safeArgs = getArgSafety(state, func, call.arguments, false);
11019
- params = Object.fromEntries(func.node.params.map((param, i) => {
11020
- const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
11021
- ? i
11022
- : -1;
11023
- const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11024
- return [name, argnum];
11025
- }));
11026
- }
11041
+ let failed = false;
11027
11042
  const pre = state.pre;
11028
11043
  const post = state.post;
11044
+ state.inlining = true;
11029
11045
  try {
11030
11046
  state.pre = (node) => {
11047
+ if (failed)
11048
+ return [];
11031
11049
  node.start = call.start;
11032
11050
  node.end = call.end;
11033
11051
  node.loc = call.loc;
@@ -11039,6 +11057,11 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11039
11057
  const { map } = locals;
11040
11058
  if (!map)
11041
11059
  throw new Error("No local variable map!");
11060
+ // We still need to keep track of every local name that was
11061
+ // already in use, but we don't want to use any of its renames.
11062
+ // We also want to know whether a local is from the function being
11063
+ // inlined, or the calling function, so set every element to false.
11064
+ Object.keys(map).forEach((key) => (map[key] = false));
11042
11065
  const declarations = func.node.params
11043
11066
  .map((param, i) => {
11044
11067
  const paramName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
@@ -11063,6 +11086,8 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11063
11086
  return result;
11064
11087
  };
11065
11088
  state.post = (node) => {
11089
+ if (failed)
11090
+ return post(node, state);
11066
11091
  let replacement = null;
11067
11092
  switch (node.type) {
11068
11093
  case "Identifier": {
@@ -11077,26 +11102,34 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
11077
11102
  }
11078
11103
  replacement = fixNodeScope(state, node, func.stack);
11079
11104
  if (!replacement) {
11080
- throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
11105
+ failed = true;
11106
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11107
+ return post(node, state);
11081
11108
  }
11082
11109
  break;
11083
11110
  }
11084
11111
  }
11085
- return post(replacement || node, state) || replacement;
11112
+ const ret = post(replacement || node, state);
11113
+ return ret === false || ret ? ret : replacement;
11086
11114
  };
11087
- return state.traverse(root) || null;
11088
- }
11089
- catch (ex) {
11090
- if (ex instanceof Error) {
11091
- if (ex.message.startsWith("Inliner: ")) {
11092
- return null;
11093
- }
11115
+ let ret = state.traverse(root);
11116
+ if (failed) {
11117
+ return null;
11118
+ }
11119
+ if (ret === null) {
11120
+ ret = root;
11121
+ }
11122
+ if (!ret) {
11123
+ inlineDiagnostic(state, func, call, `Internal error`);
11124
+ return null;
11094
11125
  }
11095
- throw ex;
11126
+ inlineDiagnostic(state, func, call, null);
11127
+ return ret;
11096
11128
  }
11097
11129
  finally {
11098
11130
  state.pre = pre;
11099
11131
  state.post = post;
11132
+ delete state.inlining;
11100
11133
  }
11101
11134
  }
11102
11135
  function unused(expression, top) {
@@ -11115,7 +11148,16 @@ function unused(expression, top) {
11115
11148
  case "UnaryExpression":
11116
11149
  return unused(expression.argument);
11117
11150
  case "MemberExpression":
11118
- return unused(expression.object).concat(unused(expression.property));
11151
+ if (expression.computed) {
11152
+ return unused(expression.object).concat(unused(expression.property));
11153
+ }
11154
+ return unused(expression.object);
11155
+ case "ArrayExpression":
11156
+ return expression.elements.map((e) => unused(e)).flat(1);
11157
+ case "ObjectExpression":
11158
+ return expression.properties
11159
+ .map((p) => unused(p.key).concat(unused(p.value)))
11160
+ .flat(1);
11119
11161
  }
11120
11162
  return top
11121
11163
  ? null
@@ -11123,30 +11165,98 @@ function unused(expression, top) {
11123
11165
  {
11124
11166
  type: "ExpressionStatement",
11125
11167
  expression,
11168
+ start: expression.start,
11169
+ end: expression.end,
11170
+ loc: expression.loc,
11126
11171
  },
11127
11172
  ];
11128
11173
  }
11129
- function inlineWithArgs(state, func, call) {
11174
+ function diagnostic(state, loc, message) {
11175
+ if (!loc || !loc.source)
11176
+ return;
11177
+ const source = loc.source;
11178
+ if (!state.diagnostics)
11179
+ state.diagnostics = {};
11180
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.diagnostics, source)) {
11181
+ if (!message)
11182
+ return;
11183
+ state.diagnostics[source] = [];
11184
+ }
11185
+ const diags = state.diagnostics[source];
11186
+ let index = diags.findIndex((item) => item.loc === loc);
11187
+ if (message) {
11188
+ if (index < 0)
11189
+ index = diags.length;
11190
+ diags[index] = { type: "INFO", loc, message };
11191
+ }
11192
+ else if (index >= 0) {
11193
+ diags.splice(index, 1);
11194
+ }
11195
+ }
11196
+ function inlineDiagnostic(state, func, call, message) {
11197
+ if (inlineRequested(state, func)) {
11198
+ diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
11199
+ }
11200
+ }
11201
+ function inlineWithArgs(state, func, call, context) {
11130
11202
  if (!func.node || !func.node.body) {
11131
11203
  return null;
11132
11204
  }
11133
11205
  let retStmtCount = 0;
11134
- (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
11135
- node.type === "ReturnStatement" && retStmtCount++;
11136
- });
11137
- if (retStmtCount > 1 ||
11138
- (retStmtCount === 1 &&
11139
- func.node.body.body.slice(-1)[0].type !== "ReturnStatement")) {
11140
- return null;
11206
+ if (context.type === "ReturnStatement") {
11207
+ const last = func.node.body.body.slice(-1)[0];
11208
+ if (!last || last.type !== "ReturnStatement") {
11209
+ inlineDiagnostic(state, func, call, "Function didn't end with a return statement");
11210
+ return null;
11211
+ }
11212
+ }
11213
+ else {
11214
+ (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
11215
+ node.type === "ReturnStatement" && retStmtCount++;
11216
+ });
11217
+ if (retStmtCount > 1) {
11218
+ inlineDiagnostic(state, func, call, "Function had more than one return statement");
11219
+ }
11220
+ else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
11221
+ inlineDiagnostic(state, func, call, "Function did not have a return statement");
11222
+ return null;
11223
+ }
11224
+ if (retStmtCount === 1) {
11225
+ const last = func.node.body.body.slice(-1)[0];
11226
+ if (!last ||
11227
+ last.type !== "ReturnStatement" ||
11228
+ (context.type === "AssignmentExpression" && !last.argument)) {
11229
+ inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
11230
+ return null;
11231
+ }
11232
+ }
11141
11233
  }
11142
11234
  const body = JSON.parse(JSON.stringify(func.node.body));
11143
- processInlineBody(state, func, call, body, func.node.params.length ? false : true);
11144
- if (retStmtCount) {
11235
+ const safeArgs = getArgSafety(state, func, call.arguments, false);
11236
+ const params = Object.fromEntries(func.node.params.map((param, i) => {
11237
+ const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
11238
+ ? i
11239
+ : -1;
11240
+ const name = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11241
+ return [name, argnum];
11242
+ }));
11243
+ if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
11244
+ return null;
11245
+ }
11246
+ diagnostic(state, call.loc, null);
11247
+ if (context.type !== "ReturnStatement" && retStmtCount) {
11145
11248
  const last = body.body[body.body.length - 1];
11146
11249
  if (last.type != "ReturnStatement") {
11147
11250
  throw new Error("ReturnStatement got lost!");
11148
11251
  }
11149
- if (last.argument) {
11252
+ if (context.type === "AssignmentExpression") {
11253
+ context.right = last.argument;
11254
+ body.body[body.body.length - 1] = {
11255
+ type: "ExpressionStatement",
11256
+ expression: context,
11257
+ };
11258
+ }
11259
+ else if (last.argument) {
11150
11260
  const side_exprs = unused(last.argument);
11151
11261
  body.body.splice(body.body.length - 1, 1, ...side_exprs);
11152
11262
  }
@@ -11156,13 +11266,13 @@ function inlineWithArgs(state, func, call) {
11156
11266
  }
11157
11267
  return body;
11158
11268
  }
11159
- function inlineFunction(state, func, call, inlineStatus) {
11160
- if (inlineStatus == InlineStatus.AsStatement) {
11161
- return inlineWithArgs(state, func, call);
11269
+ function inlineFunction(state, func, call, context) {
11270
+ if (context) {
11271
+ return inlineWithArgs(state, func, call, context);
11162
11272
  }
11163
11273
  const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
11164
11274
  const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
11165
- return processInlineBody(state, func, call, retArg, true, params) || retArg;
11275
+ return processInlineBody(state, func, call, retArg, true, params);
11166
11276
  }
11167
11277
  function applyTypeIfNeeded(node) {
11168
11278
  if ("enumType" in node && node.enumType) {
@@ -11177,13 +11287,14 @@ function applyTypeIfNeeded(node) {
11177
11287
  }
11178
11288
  function fixNodeScope(state, lookupNode, nodeStack) {
11179
11289
  if (lookupNode.type === "Identifier") {
11180
- for (let i = state.stack.length; --i > nodeStack.length;) {
11181
- const si = state.stack[i];
11182
- if ((0,external_api_cjs_namespaceObject.hasProperty)(si.decls, lookupNode.name)) {
11183
- // its a local from the inlined function.
11184
- // Nothing to do.
11185
- return lookupNode;
11186
- }
11290
+ const locals = state.localsStack[state.localsStack.length - 1];
11291
+ const { map } = locals;
11292
+ if (!map)
11293
+ throw new Error("No local variable map!");
11294
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(map, lookupNode.name) && map[lookupNode.name] !== false) {
11295
+ // map[name] !== false means its an entry that was created during inlining
11296
+ // so its definitely one of our locals.
11297
+ return lookupNode;
11187
11298
  }
11188
11299
  }
11189
11300
  const [, original] = state.lookup(lookupNode, null, nodeStack);
@@ -11400,12 +11511,12 @@ async function analyze(fnMap) {
11400
11511
  shouldExclude(node) {
11401
11512
  if ("attrs" in node &&
11402
11513
  node.attrs &&
11403
- "attrs" in node.attrs &&
11404
- node.attrs.attrs &&
11514
+ "attributes" in node.attrs &&
11515
+ node.attrs.attributes &&
11405
11516
  node.loc?.source) {
11406
11517
  const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
11407
11518
  if (excludeAnnotations) {
11408
- return node.attrs.attrs.reduce((drop, attr) => {
11519
+ return node.attrs.attributes.elements.reduce((drop, attr) => {
11409
11520
  if (attr.type != "UnaryExpression")
11410
11521
  return drop;
11411
11522
  if (attr.argument.type != "Identifier")
@@ -11656,13 +11767,15 @@ function optimizeNode(node) {
11656
11767
  return null;
11657
11768
  }
11658
11769
  function evaluateFunction(func, args) {
11659
- if (args && args.length != func.params.length) {
11770
+ if (!func.body || (args && args.length != func.params.length)) {
11660
11771
  return false;
11661
11772
  }
11662
11773
  const paramValues = args &&
11663
11774
  Object.fromEntries(func.params.map((p, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(p), args[i]]));
11664
11775
  let ret = null;
11665
- const body = args ? JSON.parse(JSON.stringify(func.body)) : func.body;
11776
+ const body = args
11777
+ ? JSON.parse(JSON.stringify(func.body))
11778
+ : func.body;
11666
11779
  try {
11667
11780
  (0,external_api_cjs_namespaceObject.traverseAst)(body, (node) => {
11668
11781
  switch (node.type) {
@@ -11705,6 +11818,13 @@ function evaluateFunction(func, args) {
11705
11818
  return false;
11706
11819
  }
11707
11820
  }
11821
+ function markFunctionCalled(state, func) {
11822
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, func.id.name)) {
11823
+ state.calledFunctions[func.id.name] = [func];
11824
+ return;
11825
+ }
11826
+ (0,external_util_cjs_namespaceObject.pushUnique)(state.calledFunctions[func.id.name], func);
11827
+ }
11708
11828
  async function optimizeMonkeyC(fnMap) {
11709
11829
  const state = {
11710
11830
  ...(await analyze(fnMap)),
@@ -11712,7 +11832,13 @@ async function optimizeMonkeyC(fnMap) {
11712
11832
  exposed: {},
11713
11833
  calledFunctions: {},
11714
11834
  };
11715
- const replace = (node, obj) => {
11835
+ const replace = (node) => {
11836
+ if (node === false || node === null)
11837
+ return node;
11838
+ const rep = state.traverse(node);
11839
+ return rep === false || rep ? rep : node;
11840
+ };
11841
+ const inPlaceReplacement = (node, obj) => {
11716
11842
  for (const k of Object.keys(node)) {
11717
11843
  delete node[k];
11718
11844
  }
@@ -11737,7 +11863,7 @@ async function optimizeMonkeyC(fnMap) {
11737
11863
  if (!obj) {
11738
11864
  return false;
11739
11865
  }
11740
- replace(node, obj);
11866
+ inPlaceReplacement(node, obj);
11741
11867
  return true;
11742
11868
  };
11743
11869
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
@@ -11753,8 +11879,8 @@ async function optimizeMonkeyC(fnMap) {
11753
11879
  if ((0,external_api_cjs_namespaceObject.hasProperty)(state.exposed, func.id.name))
11754
11880
  return true;
11755
11881
  if (func.attrs &&
11756
- func.attrs.attrs &&
11757
- func.attrs.attrs.some((attr) => {
11882
+ func.attrs.attributes &&
11883
+ func.attrs.attributes.elements.some((attr) => {
11758
11884
  if (attr.type != "UnaryExpression")
11759
11885
  return false;
11760
11886
  if (attr.argument.type != "Identifier")
@@ -11863,7 +11989,7 @@ async function optimizeMonkeyC(fnMap) {
11863
11989
  if (map) {
11864
11990
  if ((0,external_api_cjs_namespaceObject.hasProperty)(map, node.name)) {
11865
11991
  const name = map[node.name];
11866
- if (name !== true) {
11992
+ if (typeof name === "string") {
11867
11993
  node.name = name;
11868
11994
  }
11869
11995
  }
@@ -11914,10 +12040,7 @@ async function optimizeMonkeyC(fnMap) {
11914
12040
  used = checkInherited(parent, node.id.name);
11915
12041
  }
11916
12042
  if (used) {
11917
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, node.id.name)) {
11918
- state.calledFunctions[node.id.name] = [];
11919
- }
11920
- state.calledFunctions[node.id.name].push(node);
12043
+ markFunctionCalled(state, node);
11921
12044
  }
11922
12045
  }
11923
12046
  }
@@ -11930,8 +12053,7 @@ async function optimizeMonkeyC(fnMap) {
11930
12053
  }
11931
12054
  const opt = optimizeNode(node);
11932
12055
  if (opt) {
11933
- replace(node, opt);
11934
- return null;
12056
+ return replace(opt);
11935
12057
  }
11936
12058
  switch (node.type) {
11937
12059
  case "ConditionalExpression":
@@ -11941,7 +12063,7 @@ async function optimizeMonkeyC(fnMap) {
11941
12063
  const rep = node.test.value ? node.consequent : node.alternate;
11942
12064
  if (!rep)
11943
12065
  return false;
11944
- replace(node, rep);
12066
+ return replace(rep);
11945
12067
  }
11946
12068
  break;
11947
12069
  case "WhileStatement":
@@ -11954,27 +12076,50 @@ async function optimizeMonkeyC(fnMap) {
11954
12076
  return node.body;
11955
12077
  }
11956
12078
  break;
11957
- case "CallExpression": {
11958
- const ret = optimizeCall(state, node, false);
11959
- if (ret) {
11960
- replace(node, ret);
12079
+ case "ReturnStatement":
12080
+ if (node.argument && node.argument.type === "CallExpression") {
12081
+ return replace(optimizeCall(state, node.argument, node));
11961
12082
  }
11962
12083
  break;
12084
+ case "CallExpression": {
12085
+ return replace(optimizeCall(state, node, null));
11963
12086
  }
12087
+ case "AssignmentExpression":
12088
+ if (node.operator === "=" &&
12089
+ node.left.type === "Identifier" &&
12090
+ node.right.type === "Identifier" &&
12091
+ node.left.name === node.right.name) {
12092
+ return { type: "Literal", value: null, raw: "null" };
12093
+ }
12094
+ break;
11964
12095
  case "ExpressionStatement":
11965
12096
  if (node.expression.type === "CallExpression") {
11966
- const ret = optimizeCall(state, node.expression, true);
11967
- if (ret) {
11968
- if (ret.type === "BlockStatement") {
11969
- return ret;
12097
+ return replace(optimizeCall(state, node.expression, node));
12098
+ }
12099
+ else if (node.expression.type === "AssignmentExpression") {
12100
+ if (node.expression.right.type === "CallExpression") {
12101
+ let ok = false;
12102
+ if (node.expression.left.type === "Identifier") {
12103
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(topLocals().map, node.expression.left.type)) {
12104
+ ok = true;
12105
+ }
12106
+ }
12107
+ if (!ok && node.expression.operator == "=") {
12108
+ const [, result] = state.lookup(node.expression.left);
12109
+ ok = result != null;
12110
+ }
12111
+ if (ok) {
12112
+ return replace(optimizeCall(state, node.expression.right, node.expression));
11970
12113
  }
11971
- node.expression = ret;
11972
12114
  }
11973
12115
  }
11974
12116
  else {
11975
12117
  const ret = unused(node.expression, true);
11976
12118
  if (ret) {
11977
- return ret;
12119
+ return ret
12120
+ .map(replace)
12121
+ .flat(1)
12122
+ .filter((s) => !!s);
11978
12123
  }
11979
12124
  }
11980
12125
  break;
@@ -11984,6 +12129,11 @@ async function optimizeMonkeyC(fnMap) {
11984
12129
  Object.values(fnMap).forEach((f) => {
11985
12130
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
11986
12131
  });
12132
+ state.calledFunctions = {};
12133
+ state.exposed = {};
12134
+ Object.values(fnMap).forEach((f) => {
12135
+ (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
12136
+ });
11987
12137
  delete state.pre;
11988
12138
  delete state.post;
11989
12139
  const cleanup = (node) => {
@@ -12012,19 +12162,24 @@ async function optimizeMonkeyC(fnMap) {
12012
12162
  if (!node.body.members.length) {
12013
12163
  if (!node.id)
12014
12164
  return false;
12015
- if (!node.body["enumType"]) {
12165
+ if (!node.body.enumType) {
12016
12166
  throw new Error("Missing enumType on optimized enum");
12017
12167
  }
12018
- replace(node, {
12168
+ return {
12019
12169
  type: "TypedefDeclaration",
12020
12170
  id: node.id,
12021
12171
  ts: {
12022
12172
  type: "UnaryExpression",
12023
- argument: { type: "TypeSpecList", ts: [node.body.enumType] },
12173
+ argument: {
12174
+ type: "TypeSpecList",
12175
+ ts: [
12176
+ node.body.enumType,
12177
+ ],
12178
+ },
12024
12179
  prefix: true,
12025
12180
  operator: " as",
12026
12181
  },
12027
- });
12182
+ };
12028
12183
  }
12029
12184
  break;
12030
12185
  case "VariableDeclaration": {
@@ -12059,9 +12214,12 @@ async function optimizeMonkeyC(fnMap) {
12059
12214
  return ret;
12060
12215
  });
12061
12216
  });
12217
+ return state.diagnostics;
12062
12218
  }
12063
- function optimizeCall(state, node, asStatement) {
12064
- const [name, callees] = state.lookup(node.callee);
12219
+ function optimizeCall(state, node, context) {
12220
+ const [name, results] = state.lookup(node.callee);
12221
+ const callees = results &&
12222
+ results.filter((c) => c.type === "FunctionDeclaration");
12065
12223
  if (!callees || !callees.length) {
12066
12224
  const n = name ||
12067
12225
  ("name" in node.callee && node.callee.name) ||
@@ -12080,7 +12238,8 @@ function optimizeCall(state, node, asStatement) {
12080
12238
  }
12081
12239
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
12082
12240
  const callee = callees[0].node;
12083
- if (callee.optimizable &&
12241
+ if (!context &&
12242
+ callee.optimizable &&
12084
12243
  !callee.hasOverride &&
12085
12244
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12086
12245
  const ret = evaluateFunction(callee, node.arguments);
@@ -12088,19 +12247,14 @@ function optimizeCall(state, node, asStatement) {
12088
12247
  return ret;
12089
12248
  }
12090
12249
  }
12091
- const inlineStatus = shouldInline(state, callees[0], node.arguments);
12092
- if (inlineStatus === InlineStatus.AsExpression ||
12093
- (asStatement && inlineStatus === InlineStatus.AsStatement)) {
12094
- const ret = inlineFunction(state, callees[0], node, inlineStatus);
12250
+ if (shouldInline(state, callees[0], node, context)) {
12251
+ const ret = inlineFunction(state, callees[0], node, context);
12095
12252
  if (ret) {
12096
12253
  return ret;
12097
12254
  }
12098
12255
  }
12099
12256
  }
12100
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
12101
- state.calledFunctions[name] = [];
12102
- }
12103
- callees.forEach((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && state.calledFunctions[name].push(c.node));
12257
+ callees.forEach((c) => markFunctionCalled(state, c.node));
12104
12258
  return null;
12105
12259
  }
12106
12260
 
@@ -12112,12 +12266,31 @@ function pragmaChecker(ast) {
12112
12266
  return;
12113
12267
  let index = -1;
12114
12268
  let comment;
12269
+ let matchers;
12115
12270
  const next = () => {
12116
12271
  while (++index < comments.length) {
12117
12272
  comment = comments[index];
12118
- if (comment.value.match(/^\s*@match\s+/)) {
12273
+ let match = comment.value.match(/^\s*@match\s+(.+)/);
12274
+ if (!match)
12275
+ continue;
12276
+ let str = match[1];
12277
+ matchers = [];
12278
+ while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
12279
+ matchers.push({ quote: match[1], needle: match[2] });
12280
+ str = str.substring(match[0].length);
12281
+ if (!str.length)
12282
+ break;
12283
+ }
12284
+ if (!str.length)
12119
12285
  break;
12286
+ if (!matchers.length) {
12287
+ match = str.match(/^(\S+)\s+$/);
12288
+ if (match) {
12289
+ matchers.push({ quote: '"', needle: match[1] });
12290
+ break;
12291
+ }
12120
12292
  }
12293
+ throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12121
12294
  }
12122
12295
  };
12123
12296
  next();
@@ -12125,29 +12298,23 @@ function pragmaChecker(ast) {
12125
12298
  if (index >= comments.length)
12126
12299
  return false;
12127
12300
  if (node.start && node.start >= (comment.end || Infinity)) {
12128
- let match = comment.value.match(/^\s*@match\s+([/%&#@"])(.+(?<!\\)(?:\\{2})*)\1\s+$/) || comment.value.match(/^\s*@match\s+(\S+)\s+$/);
12129
- if (!match) {
12130
- throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12131
- }
12301
+ const { quote, needle } = matchers.shift();
12132
12302
  const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/[\r\n]/g, " ");
12133
12303
  let found = false;
12134
- let needle = match[1];
12135
- if (match.length == 2) {
12304
+ if (quote == '"') {
12136
12305
  found = haystack.includes(needle);
12137
12306
  }
12138
12307
  else {
12139
- if (needle == '"') {
12140
- found = haystack.includes((needle = match[2]));
12141
- }
12142
- else {
12143
- const re = new RegExp((needle = match[2]));
12144
- found = re.test(haystack);
12145
- }
12308
+ const re = new RegExp(needle);
12309
+ found = re.test(haystack);
12146
12310
  }
12147
12311
  if (!found) {
12148
- throw new Error(`Didn't find '${needle}' in ${comment.loc.source}:${comment.loc.start.line}`);
12312
+ throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
12313
+ }
12314
+ if (!matchers.length) {
12315
+ next();
12149
12316
  }
12150
- next();
12317
+ return false;
12151
12318
  }
12152
12319
  return null;
12153
12320
  });
@@ -12236,7 +12403,7 @@ async function buildOptimizedProject(product, options) {
12236
12403
  config.releaseBuild = true;
12237
12404
  }
12238
12405
  }
12239
- const { jungleFiles, program, hasTests } = await generateOptimizedProject(config);
12406
+ const { jungleFiles, program, hasTests, diagnostics } = await generateOptimizedProject(config);
12240
12407
  config.jungleFiles = jungleFiles;
12241
12408
  let bin = config.buildDir || "bin";
12242
12409
  let name = `optimized-${program}.prg`;
@@ -12255,6 +12422,7 @@ async function buildOptimizedProject(product, options) {
12255
12422
  delete config.testBuild;
12256
12423
  return build_project(product, config).then((result) => ({
12257
12424
  hasTests,
12425
+ diagnostics,
12258
12426
  ...result,
12259
12427
  }));
12260
12428
  }
@@ -12393,6 +12561,7 @@ async function generateOptimizedProject(options) {
12393
12561
  (await checkManifest(xml, targets.map((t) => t.product)))) &&
12394
12562
  !dropBarrels;
12395
12563
  let hasTests = false;
12564
+ let diagnostics = {};
12396
12565
  const promises = Object.keys(buildConfigs)
12397
12566
  .sort()
12398
12567
  .map((key) => {
@@ -12410,7 +12579,13 @@ async function generateOptimizedProject(options) {
12410
12579
  e.products = products[key];
12411
12580
  throw e;
12412
12581
  })
12413
- .then((t) => t && (hasTests = true))
12582
+ .then((t) => {
12583
+ if (t.hasTests)
12584
+ hasTests = true;
12585
+ if (t.diagnostics) {
12586
+ diagnostics = { ...diagnostics, ...t.diagnostics };
12587
+ }
12588
+ })
12414
12589
  : promises_namespaceObject.rm(external_path_.resolve(workspace, outputPath), {
12415
12590
  recursive: true,
12416
12591
  force: true,
@@ -12481,6 +12656,7 @@ async function generateOptimizedProject(options) {
12481
12656
  xml,
12482
12657
  program: external_path_.basename(external_path_.dirname(manifest)),
12483
12658
  hasTests,
12659
+ diagnostics,
12484
12660
  };
12485
12661
  }
12486
12662
  async function fileInfoFromConfig(workspace, output, buildConfig, extraExcludes) {
@@ -12559,7 +12735,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12559
12735
  const actualOptimizedFiles = (await (0,external_util_cjs_namespaceObject.globa)(external_path_.join(output, "**", "*.mc"), { mark: true }))
12560
12736
  .filter((file) => !file.endsWith("/"))
12561
12737
  .sort();
12562
- const { hasTests, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12738
+ const { hasTests, diagnostics: prevDiagnostics, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12563
12739
  .catch(() => "{}"));
12564
12740
  // check that the set of files thats actually there is the same as the
12565
12741
  // set of files we're going to generate (in case eg a jungle file change
@@ -12577,13 +12753,13 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12577
12753
  // the oldest optimized file, we don't need to regenerate
12578
12754
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12579
12755
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12580
- if (source_time < opt_time && 1654389404778 < opt_time) {
12581
- return hasTests;
12756
+ if (source_time < opt_time && 1654811717108 < opt_time) {
12757
+ return { hasTests, diagnostics: prevDiagnostics };
12582
12758
  }
12583
12759
  }
12584
12760
  await promises_namespaceObject.rm(output, { recursive: true, force: true });
12585
12761
  await promises_namespaceObject.mkdir(output, { recursive: true });
12586
- await optimizeMonkeyC(fnMap);
12762
+ const diagnostics = await optimizeMonkeyC(fnMap);
12587
12763
  return Promise.all(Object.values(fnMap).map(async (info) => {
12588
12764
  const name = info.output;
12589
12765
  const dir = external_path_.dirname(name);
@@ -12598,9 +12774,10 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12598
12774
  const hasTests = results.some((v) => v);
12599
12775
  return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
12600
12776
  hasTests,
12777
+ diagnostics,
12601
12778
  ...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
12602
12779
  }))
12603
- .then(() => hasTests);
12780
+ .then(() => ({ hasTests, diagnostics }));
12604
12781
  });
12605
12782
  }
12606
12783
  async function getProjectAnalysis(targets, analysis, options) {