@markw65/monkeyc-optimizer 1.0.16 → 1.0.19

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.
@@ -1,4 +1,4 @@
1
- 0 && (module.exports = {copyRecursiveAsNeeded,get_jungle,launchSimulator,manifestProducts,mctree,simulateProgram,defaultConfig,isErrorWithLocation,buildOptimizedProject,generateOptimizedProject,getProjectAnalysis,generateApiMirTests});
1
+ 0 && (module.exports = {buildOptimizedProject,copyRecursiveAsNeeded,defaultConfig,generateApiMirTests,generateOptimizedProject,getProjectAnalysis,get_jungle,isErrorWithLocation,launchSimulator,manifestProducts,mctree,simulateProgram});
2
2
  /******/ (() => { // webpackBootstrap
3
3
  /******/ var __webpack_modules__ = ({
4
4
 
@@ -10791,11 +10791,67 @@ function simulateProgram(prg, device, test) {
10791
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(() => { }));
10792
10792
  }
10793
10793
 
10794
+ ;// CONCATENATED MODULE: ./src/variable-renamer.ts
10795
+
10796
+ function renameVariable(state, locals, declName) {
10797
+ const map = locals.map;
10798
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(map, declName))
10799
+ return null;
10800
+ let suffix = 0;
10801
+ let node_name = declName;
10802
+ const match = node_name.match(/^pmcr_(.*)_(\d+)$/);
10803
+ if (match) {
10804
+ node_name = match[1];
10805
+ suffix = parseInt(match[2], 10) + 1;
10806
+ }
10807
+ if (!locals.inners) {
10808
+ // find all the names declared in this scope, to avoid
10809
+ // more conflicts
10810
+ locals.inners = {};
10811
+ const inners = locals.inners;
10812
+ (0,external_api_cjs_namespaceObject.traverseAst)(locals.node, (node) => {
10813
+ if (node.type === "VariableDeclarator") {
10814
+ inners[(0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id)] = true;
10815
+ }
10816
+ });
10817
+ }
10818
+ let name;
10819
+ while (true) {
10820
+ name = `pmcr_${node_name}_${suffix}`;
10821
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(map, name) && !(0,external_api_cjs_namespaceObject.hasProperty)(locals.inners, name)) {
10822
+ // we also need to ensure that we don't hide the name of
10823
+ // an outer module, class, function, enum or variable,
10824
+ // since someone might want to access it from this scope.
10825
+ let ok = false;
10826
+ let i;
10827
+ for (i = state.stack.length; i--;) {
10828
+ const elm = state.stack[i];
10829
+ if (ok) {
10830
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(elm.decls, name)) {
10831
+ break;
10832
+ }
10833
+ }
10834
+ else if (elm.node && elm.node.type === "FunctionDeclaration") {
10835
+ ok = true;
10836
+ }
10837
+ }
10838
+ if (i < 0) {
10839
+ break;
10840
+ }
10841
+ }
10842
+ suffix++;
10843
+ }
10844
+ map[declName] = name;
10845
+ map[name] = true;
10846
+ return name;
10847
+ }
10848
+
10794
10849
  ;// CONCATENATED MODULE: ./src/inliner.ts
10795
10850
 
10796
- function canInline(state, func, args) {
10851
+
10852
+ function getArgSafety(state, func, args, requireAll) {
10797
10853
  // determine whether decl might be changed by a function call
10798
- // during the evaluation of FunctionStateNode.
10854
+ // or assignment during the evaluation of FunctionStateNode.
10799
10855
  const getSafety = (decl) => {
10800
10856
  // enums are constant, they cant change
10801
10857
  if (decl.type === "EnumStringMember")
@@ -10825,22 +10881,28 @@ function canInline(state, func, args) {
10825
10881
  case "Identifier":
10826
10882
  case "MemberExpression": {
10827
10883
  const [, results] = state.lookup(arg);
10828
- if (!results || results.length !== 1)
10829
- return false;
10884
+ if (!results || results.length !== 1) {
10885
+ safeArgs.push(null);
10886
+ return !requireAll;
10887
+ }
10830
10888
  const safety = getSafety(results[0]);
10831
- if (safety === null)
10832
- return false;
10833
- if (!safety)
10834
- allSafe = false;
10835
10889
  safeArgs.push(safety);
10890
+ if (!safety) {
10891
+ allSafe = false;
10892
+ if (safety === null) {
10893
+ return !requireAll;
10894
+ }
10895
+ }
10836
10896
  return true;
10837
10897
  }
10838
10898
  }
10839
- return false;
10899
+ allSafe = false;
10900
+ safeArgs.push(null);
10901
+ return !requireAll;
10840
10902
  })) {
10841
10903
  return false;
10842
10904
  }
10843
- if (allSafe)
10905
+ if (allSafe && requireAll)
10844
10906
  return true;
10845
10907
  let callSeen = false;
10846
10908
  let ok = true;
@@ -10863,6 +10925,14 @@ function canInline(state, func, args) {
10863
10925
  .map(([key]) => key);
10864
10926
  }, (node) => {
10865
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
10866
10936
  case "CallExpression":
10867
10937
  case "NewExpression":
10868
10938
  callSeen = true;
@@ -10871,11 +10941,18 @@ function canInline(state, func, args) {
10871
10941
  if (callSeen &&
10872
10942
  (0,external_api_cjs_namespaceObject.hasProperty)(params, node.name) &&
10873
10943
  !safeArgs[params[node.name]]) {
10874
- ok = false;
10944
+ safeArgs[params[node.name]] = null;
10875
10945
  }
10876
10946
  }
10877
10947
  });
10878
- return ok;
10948
+ return safeArgs;
10949
+ }
10950
+ function canInline(state, func, args) {
10951
+ const safeArgs = getArgSafety(state, func, args, true);
10952
+ if (safeArgs === true || safeArgs === false) {
10953
+ return safeArgs;
10954
+ }
10955
+ return safeArgs.every((arg) => arg !== null);
10879
10956
  }
10880
10957
  function inliningLooksUseful(func, node) {
10881
10958
  while (true) {
@@ -10900,77 +10977,288 @@ function inliningLooksUseful(func, node) {
10900
10977
  }
10901
10978
  return false;
10902
10979
  }
10903
- function shouldInline(state, func, args) {
10904
- if (!func.node.body ||
10905
- func.node.body.body.length !== 1 ||
10906
- func.node.body.body[0].type !== "ReturnStatement" ||
10907
- !func.node.body.body[0].argument ||
10908
- func.node.params.length !== args.length) {
10909
- return false;
10910
- }
10911
- const autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
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
+ function inlineRequested(state, func) {
10912
10987
  const excludeAnnotations = (func.node.loc?.source &&
10913
- state.fnMap[func.node.loc?.source].excludeAnnotations) ||
10988
+ state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
10914
10989
  {};
10915
- return ((autoInline ||
10916
- (func.node.attrs &&
10917
- func.node.attrs.attrs &&
10918
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
10919
- (attr.argument.name === "inline" ||
10920
- (attr.argument.name.startsWith("inline_") &&
10921
- (0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7))))))) &&
10922
- (autoInline === 1 || canInline(state, func, args)));
10923
- }
10924
- function inlineFunction(state, func, call) {
10925
- const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
10926
- const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
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) {
11001
+ let autoInline = false;
11002
+ let inlineAsExpression = false;
11003
+ const args = call.arguments;
11004
+ if (func.node.body &&
11005
+ func.node.body.body.length === 1 &&
11006
+ func.node.body.body[0].type === "ReturnStatement" &&
11007
+ func.node.body.body[0].argument &&
11008
+ func.node.params.length === args.length) {
11009
+ inlineAsExpression = true;
11010
+ autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
11011
+ }
11012
+ if (autoInline === 1) {
11013
+ return true;
11014
+ }
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;
11026
+ }
11027
+ return false;
11028
+ }
11029
+ function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
11030
+ let failed = false;
11031
+ const pre = state.pre;
11032
+ const post = state.post;
10927
11033
  try {
10928
- const result = (0,external_api_cjs_namespaceObject.traverseAst)(retArg, (node) => {
10929
- switch (node.type) {
10930
- case "MemberExpression":
10931
- if (!node.computed) {
10932
- return ["object"];
10933
- }
10934
- break;
10935
- case "BinaryExpression":
10936
- if (node.operator === "as") {
10937
- return ["left"];
10938
- }
10939
- break;
10940
- case "UnaryExpression":
10941
- if (node.operator === " as") {
10942
- return [];
10943
- }
11034
+ state.pre = (node) => {
11035
+ if (failed)
11036
+ return [];
11037
+ node.start = call.start;
11038
+ node.end = call.end;
11039
+ node.loc = call.loc;
11040
+ if (node === insertedVariableDecls)
11041
+ return false;
11042
+ const result = pre(node, state);
11043
+ if (!insertedVariableDecls && node.type === "BlockStatement") {
11044
+ const locals = state.localsStack[state.localsStack.length - 1];
11045
+ const { map } = locals;
11046
+ if (!map)
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));
11053
+ const declarations = func.node.params
11054
+ .map((param, i) => {
11055
+ const paramName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11056
+ if (params[paramName] >= 0)
11057
+ return null;
11058
+ const name = renameVariable(state, locals, paramName) || paramName;
11059
+ return {
11060
+ type: "VariableDeclarator",
11061
+ id: { type: "Identifier", name },
11062
+ kind: "var",
11063
+ init: call.arguments[i],
11064
+ };
11065
+ })
11066
+ .filter((n) => n != null);
11067
+ insertedVariableDecls = {
11068
+ type: "VariableDeclaration",
11069
+ declarations,
11070
+ kind: "var",
11071
+ };
11072
+ node.body.unshift(insertedVariableDecls);
10944
11073
  }
10945
- return null;
10946
- }, (node) => {
11074
+ return result;
11075
+ };
11076
+ state.post = (node) => {
11077
+ if (failed)
11078
+ return null;
11079
+ let replacement = null;
10947
11080
  switch (node.type) {
10948
11081
  case "Identifier": {
11082
+ if (state.inType)
11083
+ break;
10949
11084
  if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
10950
- return call.arguments[params[node.name]];
11085
+ const ix = params[node.name];
11086
+ if (ix >= 0) {
11087
+ replacement = call.arguments[ix];
11088
+ }
11089
+ break;
10951
11090
  }
10952
- const rep = fixNodeScope(state, node, func.stack);
10953
- if (!rep) {
10954
- throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
11091
+ replacement = fixNodeScope(state, node, func.stack);
11092
+ if (!replacement) {
11093
+ failed = true;
11094
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
11095
+ return null;
10955
11096
  }
10956
- return rep;
11097
+ break;
10957
11098
  }
10958
11099
  }
11100
+ return post(replacement || node, state) || replacement;
11101
+ };
11102
+ let ret = state.traverse(root);
11103
+ if (failed) {
10959
11104
  return null;
10960
- }) || retArg;
10961
- result.loc = call.loc;
10962
- result.start = call.start;
10963
- result.end = call.end;
10964
- return result;
11105
+ }
11106
+ if (ret === null) {
11107
+ ret = root;
11108
+ }
11109
+ if (!ret) {
11110
+ inlineDiagnostic(state, func, call, `Internal error`);
11111
+ return null;
11112
+ }
11113
+ inlineDiagnostic(state, func, call, null);
11114
+ return ret;
10965
11115
  }
10966
- catch (ex) {
10967
- if (ex instanceof Error) {
10968
- if (ex.message.startsWith("Inliner: ")) {
11116
+ finally {
11117
+ state.pre = pre;
11118
+ state.post = post;
11119
+ }
11120
+ }
11121
+ function unused(expression, top) {
11122
+ switch (expression.type) {
11123
+ case "Literal":
11124
+ return [];
11125
+ case "Identifier":
11126
+ return [];
11127
+ case "BinaryExpression":
11128
+ if (expression.operator === "as") {
11129
+ return unused(expression.left);
11130
+ }
11131
+ // fall through
11132
+ case "LogicalExpression":
11133
+ return unused(expression.left).concat(unused(expression.right));
11134
+ case "UnaryExpression":
11135
+ return unused(expression.argument);
11136
+ case "MemberExpression":
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);
11147
+ }
11148
+ return top
11149
+ ? null
11150
+ : [
11151
+ {
11152
+ type: "ExpressionStatement",
11153
+ expression,
11154
+ start: expression.start,
11155
+ end: expression.end,
11156
+ loc: expression.loc,
11157
+ },
11158
+ ];
11159
+ }
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) {
11188
+ if (!func.node || !func.node.body) {
11189
+ return null;
11190
+ }
11191
+ let retStmtCount = 0;
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");
10969
11216
  return null;
10970
11217
  }
10971
11218
  }
10972
- throw ex;
10973
11219
  }
11220
+ const body = JSON.parse(JSON.stringify(func.node.body));
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) {
11234
+ const last = body.body[body.body.length - 1];
11235
+ if (last.type != "ReturnStatement") {
11236
+ throw new Error("ReturnStatement got lost!");
11237
+ }
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) {
11246
+ const side_exprs = unused(last.argument);
11247
+ body.body.splice(body.body.length - 1, 1, ...side_exprs);
11248
+ }
11249
+ else {
11250
+ --body.body.length;
11251
+ }
11252
+ }
11253
+ return body;
11254
+ }
11255
+ function inlineFunction(state, func, call, context) {
11256
+ if (context) {
11257
+ return inlineWithArgs(state, func, call, context);
11258
+ }
11259
+ const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
11260
+ const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
11261
+ return processInlineBody(state, func, call, retArg, true, params);
10974
11262
  }
10975
11263
  function applyTypeIfNeeded(node) {
10976
11264
  if ("enumType" in node && node.enumType) {
@@ -10984,6 +11272,17 @@ function applyTypeIfNeeded(node) {
10984
11272
  return node;
10985
11273
  }
10986
11274
  function fixNodeScope(state, lookupNode, nodeStack) {
11275
+ if (lookupNode.type === "Identifier") {
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;
11284
+ }
11285
+ }
10987
11286
  const [, original] = state.lookup(lookupNode, null, nodeStack);
10988
11287
  if (!original) {
10989
11288
  return null;
@@ -11067,6 +11366,7 @@ function fixNodeScope(state, lookupNode, nodeStack) {
11067
11366
 
11068
11367
 
11069
11368
 
11369
+
11070
11370
  function processImports(allImports, lookup) {
11071
11371
  allImports.forEach(({ node, stack }) => {
11072
11372
  const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
@@ -11080,10 +11380,13 @@ function processImports(allImports, lookup) {
11080
11380
  module.forEach((m) => {
11081
11381
  if ((0,external_api_cjs_namespaceObject.isStateNode)(m) && m.type == "ModuleDeclaration") {
11082
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);
11083
11389
  if (node.type == "ImportModule" && m.type_decls) {
11084
- if (!parent.type_decls)
11085
- parent.type_decls = {};
11086
- const tdecls = parent.type_decls;
11087
11390
  Object.entries(m.type_decls).forEach(([name, decls]) => {
11088
11391
  if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
11089
11392
  tdecls[name] = [];
@@ -11450,13 +11753,15 @@ function optimizeNode(node) {
11450
11753
  return null;
11451
11754
  }
11452
11755
  function evaluateFunction(func, args) {
11453
- if (args && args.length != func.params.length) {
11756
+ if (!func.body || (args && args.length != func.params.length)) {
11454
11757
  return false;
11455
11758
  }
11456
11759
  const paramValues = args &&
11457
11760
  Object.fromEntries(func.params.map((p, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(p), args[i]]));
11458
11761
  let ret = null;
11459
- 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;
11460
11765
  try {
11461
11766
  (0,external_api_cjs_namespaceObject.traverseAst)(body, (node) => {
11462
11767
  switch (node.type) {
@@ -11624,57 +11929,8 @@ async function optimizeMonkeyC(fnMap) {
11624
11929
  const { map } = locals;
11625
11930
  if (map) {
11626
11931
  const declName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id);
11627
- if ((0,external_api_cjs_namespaceObject.hasProperty)(map, declName)) {
11628
- // We already have a variable with this name in scope
11629
- // Recent monkeyc compilers complain, so rename it
11630
- let suffix = 0;
11631
- let node_name = declName;
11632
- const match = node_name.match(/^pmcr_(.*)_(\d+)$/);
11633
- if (match) {
11634
- node_name = match[1];
11635
- suffix = parseInt(match[2], 10) + 1;
11636
- }
11637
- if (!locals.inners) {
11638
- // find all the names declared in this scope, to avoid
11639
- // more conflicts
11640
- locals.inners = {};
11641
- const inners = locals.inners;
11642
- (0,external_api_cjs_namespaceObject.traverseAst)(locals.node, (node) => {
11643
- if (node.type === "VariableDeclarator") {
11644
- inners[(0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id)] = true;
11645
- }
11646
- });
11647
- }
11648
- let name;
11649
- while (true) {
11650
- name = `pmcr_${node_name}_${suffix}`;
11651
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(map, name) &&
11652
- !(0,external_api_cjs_namespaceObject.hasProperty)(locals.inners, name)) {
11653
- // we also need to ensure that we don't hide the name of
11654
- // an outer module, class, function, enum or variable,
11655
- // since someone might want to access it from this scope.
11656
- let ok = false;
11657
- let i;
11658
- for (i = state.stack.length; i--;) {
11659
- const elm = state.stack[i];
11660
- if (ok) {
11661
- if ((0,external_api_cjs_namespaceObject.hasProperty)(elm.decls, name)) {
11662
- break;
11663
- }
11664
- }
11665
- else if (elm.node &&
11666
- elm.node.type === "FunctionDeclaration") {
11667
- ok = true;
11668
- }
11669
- }
11670
- if (i < 0) {
11671
- break;
11672
- }
11673
- }
11674
- suffix++;
11675
- }
11676
- map[declName] = name;
11677
- map[name] = true;
11932
+ const name = renameVariable(state, locals, declName);
11933
+ if (name) {
11678
11934
  if (node.id.type === "Identifier") {
11679
11935
  node.id.name = name;
11680
11936
  }
@@ -11698,7 +11954,7 @@ async function optimizeMonkeyC(fnMap) {
11698
11954
  state.exposed[node.argument.name] = true;
11699
11955
  // In any case, we can't replace *this* use of the
11700
11956
  // symbol with its value...
11701
- return false;
11957
+ return [];
11702
11958
  }
11703
11959
  break;
11704
11960
  case "Identifier": {
@@ -11706,7 +11962,7 @@ async function optimizeMonkeyC(fnMap) {
11706
11962
  if (map) {
11707
11963
  if ((0,external_api_cjs_namespaceObject.hasProperty)(map, node.name)) {
11708
11964
  const name = map[node.name];
11709
- if (name !== true) {
11965
+ if (typeof name === "string") {
11710
11966
  node.name = name;
11711
11967
  }
11712
11968
  }
@@ -11716,7 +11972,7 @@ async function optimizeMonkeyC(fnMap) {
11716
11972
  state.exposed[node.name] = true;
11717
11973
  }
11718
11974
  }
11719
- return false;
11975
+ return [];
11720
11976
  }
11721
11977
  case "MemberExpression":
11722
11978
  if (node.property.type === "Identifier" && !node.computed) {
@@ -11797,49 +12053,58 @@ async function optimizeMonkeyC(fnMap) {
11797
12053
  return node.body;
11798
12054
  }
11799
12055
  break;
12056
+ case "ReturnStatement":
12057
+ if (node.argument && node.argument.type === "CallExpression") {
12058
+ return optimizeCall(state, node.argument, node);
12059
+ }
12060
+ break;
11800
12061
  case "CallExpression": {
11801
- const [name, callees] = state.lookup(node.callee);
11802
- if (!callees || !callees.length) {
11803
- const n = name ||
11804
- ("name" in node.callee && node.callee.name) ||
11805
- ("property" in node.callee &&
11806
- node.callee.property &&
11807
- "name" in node.callee.property &&
11808
- node.callee.property.name);
11809
- if (n) {
11810
- state.exposed[n] = true;
11811
- }
11812
- else {
11813
- // There are unnamed CallExpressions, such as new [size]
11814
- // So there's nothing to do here.
11815
- }
11816
- return null;
12062
+ const ret = optimizeCall(state, node, null);
12063
+ if (ret) {
12064
+ replace(node, ret);
11817
12065
  }
11818
- if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
11819
- const callee = callees[0].node;
11820
- if (callee.optimizable &&
11821
- !callee.hasOverride &&
11822
- node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
11823
- const ret = evaluateFunction(callee, node.arguments);
11824
- if (ret) {
11825
- replace(node, ret);
11826
- return null;
12066
+ break;
12067
+ }
12068
+ case "AssignmentExpression":
12069
+ if (node.operator === "=" &&
12070
+ node.left.type === "Identifier" &&
12071
+ node.right.type === "Identifier" &&
12072
+ node.left.name === node.right.name) {
12073
+ return { type: "Literal", value: null, raw: "null" };
12074
+ }
12075
+ break;
12076
+ case "ExpressionStatement":
12077
+ if (node.expression.type === "CallExpression") {
12078
+ return optimizeCall(state, node.expression, node);
12079
+ }
12080
+ else if (node.expression.type === "AssignmentExpression") {
12081
+ if (node.expression.right.type === "CallExpression") {
12082
+ let ok = false;
12083
+ if (node.expression.left.type === "Identifier") {
12084
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(topLocals().map, node.expression.left.type)) {
12085
+ ok = true;
12086
+ }
11827
12087
  }
11828
- }
11829
- if (shouldInline(state, callees[0], node.arguments)) {
11830
- const ret = inlineFunction(state, callees[0], node);
11831
- if (ret) {
11832
- replace(node, ret);
11833
- return null;
12088
+ if (!ok && node.expression.operator == "=") {
12089
+ const [, result] = state.lookup(node.expression.left);
12090
+ ok = result != null;
12091
+ }
12092
+ 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
+ }
11834
12098
  }
11835
12099
  }
11836
12100
  }
11837
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
11838
- state.calledFunctions[name] = [];
12101
+ else {
12102
+ const ret = unused(node.expression, true);
12103
+ if (ret) {
12104
+ return ret;
12105
+ }
11839
12106
  }
11840
- callees.forEach((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && state.calledFunctions[name].push(c.node));
11841
12107
  break;
11842
- }
11843
12108
  }
11844
12109
  return null;
11845
12110
  };
@@ -11921,6 +12186,111 @@ async function optimizeMonkeyC(fnMap) {
11921
12186
  return ret;
11922
12187
  });
11923
12188
  });
12189
+ return state.diagnostics;
12190
+ }
12191
+ function optimizeCall(state, node, context) {
12192
+ const [name, callees] = state.lookup(node.callee);
12193
+ if (!callees || !callees.length) {
12194
+ const n = name ||
12195
+ ("name" in node.callee && node.callee.name) ||
12196
+ ("property" in node.callee &&
12197
+ node.callee.property &&
12198
+ "name" in node.callee.property &&
12199
+ node.callee.property.name);
12200
+ if (n) {
12201
+ state.exposed[n] = true;
12202
+ }
12203
+ else {
12204
+ // There are unnamed CallExpressions, such as new [size]
12205
+ // So there's nothing to do here.
12206
+ }
12207
+ return null;
12208
+ }
12209
+ if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
12210
+ const callee = callees[0].node;
12211
+ if (!context &&
12212
+ callee.optimizable &&
12213
+ !callee.hasOverride &&
12214
+ node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12215
+ const ret = evaluateFunction(callee, node.arguments);
12216
+ if (ret) {
12217
+ return ret;
12218
+ }
12219
+ }
12220
+ if (shouldInline(state, callees[0], node, context)) {
12221
+ const ret = inlineFunction(state, callees[0], node, context);
12222
+ if (ret) {
12223
+ return ret;
12224
+ }
12225
+ }
12226
+ }
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));
12231
+ return null;
12232
+ }
12233
+
12234
+ ;// CONCATENATED MODULE: ./src/pragma-checker.ts
12235
+
12236
+ function pragmaChecker(ast) {
12237
+ const comments = ast.comments;
12238
+ if (!comments)
12239
+ return;
12240
+ let index = -1;
12241
+ let comment;
12242
+ let matchers;
12243
+ const next = () => {
12244
+ while (++index < comments.length) {
12245
+ comment = comments[index];
12246
+ let match = comment.value.match(/^\s*@match\s+(.+)/);
12247
+ if (!match)
12248
+ continue;
12249
+ let str = match[1];
12250
+ matchers = [];
12251
+ while ((match = str.match(/^([/%&#@"])(.+?(?<!\\)(?:\\{2})*)\1(\s+|$)/))) {
12252
+ matchers.push({ quote: match[1], needle: match[2] });
12253
+ str = str.substring(match[0].length);
12254
+ if (!str.length)
12255
+ break;
12256
+ }
12257
+ if (!str.length)
12258
+ break;
12259
+ if (!matchers.length) {
12260
+ match = str.match(/^(\S+)\s+$/);
12261
+ if (match) {
12262
+ matchers.push({ quote: '"', needle: match[1] });
12263
+ break;
12264
+ }
12265
+ }
12266
+ throw new Error(`Build pragma '${comment.value}' is invalid. In ${comment.loc.source}:${comment.loc.start.line}`);
12267
+ }
12268
+ };
12269
+ next();
12270
+ (0,external_api_cjs_namespaceObject.traverseAst)(ast, (node) => {
12271
+ if (index >= comments.length)
12272
+ return false;
12273
+ 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);
12283
+ }
12284
+ if (!found) {
12285
+ throw new Error(`Didn't find '${needle}' at ${comment.loc.source}:${comment.loc.start.line}`);
12286
+ }
12287
+ if (!matchers.length) {
12288
+ next();
12289
+ }
12290
+ return false;
12291
+ }
12292
+ return null;
12293
+ });
11924
12294
  }
11925
12295
 
11926
12296
  ;// CONCATENATED MODULE: ./src/optimizer.ts
@@ -11937,6 +12307,7 @@ async function optimizeMonkeyC(fnMap) {
11937
12307
 
11938
12308
 
11939
12309
 
12310
+
11940
12311
  function relative_path_no_dotdot(relative) {
11941
12312
  return relative.replace(/^(\.\.[\\\/])+/, (str) => `__${"dot".repeat(str.length / 3)}__${str.slice(-1)}`);
11942
12313
  }
@@ -12005,7 +12376,7 @@ async function buildOptimizedProject(product, options) {
12005
12376
  config.releaseBuild = true;
12006
12377
  }
12007
12378
  }
12008
- const { jungleFiles, program, hasTests } = await generateOptimizedProject(config);
12379
+ const { jungleFiles, program, hasTests, diagnostics } = await generateOptimizedProject(config);
12009
12380
  config.jungleFiles = jungleFiles;
12010
12381
  let bin = config.buildDir || "bin";
12011
12382
  let name = `optimized-${program}.prg`;
@@ -12024,6 +12395,7 @@ async function buildOptimizedProject(product, options) {
12024
12395
  delete config.testBuild;
12025
12396
  return build_project(product, config).then((result) => ({
12026
12397
  hasTests,
12398
+ diagnostics,
12027
12399
  ...result,
12028
12400
  }));
12029
12401
  }
@@ -12162,6 +12534,7 @@ async function generateOptimizedProject(options) {
12162
12534
  (await checkManifest(xml, targets.map((t) => t.product)))) &&
12163
12535
  !dropBarrels;
12164
12536
  let hasTests = false;
12537
+ let diagnostics = {};
12165
12538
  const promises = Object.keys(buildConfigs)
12166
12539
  .sort()
12167
12540
  .map((key) => {
@@ -12179,7 +12552,13 @@ async function generateOptimizedProject(options) {
12179
12552
  e.products = products[key];
12180
12553
  throw e;
12181
12554
  })
12182
- .then((t) => t && (hasTests = true))
12555
+ .then((t) => {
12556
+ if (t.hasTests)
12557
+ hasTests = true;
12558
+ if (t.diagnostics) {
12559
+ diagnostics = { ...diagnostics, ...t.diagnostics };
12560
+ }
12561
+ })
12183
12562
  : promises_namespaceObject.rm(external_path_.resolve(workspace, outputPath), {
12184
12563
  recursive: true,
12185
12564
  force: true,
@@ -12250,6 +12629,7 @@ async function generateOptimizedProject(options) {
12250
12629
  xml,
12251
12630
  program: external_path_.basename(external_path_.dirname(manifest)),
12252
12631
  hasTests,
12632
+ diagnostics,
12253
12633
  };
12254
12634
  }
12255
12635
  async function fileInfoFromConfig(workspace, output, buildConfig, extraExcludes) {
@@ -12328,7 +12708,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12328
12708
  const actualOptimizedFiles = (await (0,external_util_cjs_namespaceObject.globa)(external_path_.join(output, "**", "*.mc"), { mark: true }))
12329
12709
  .filter((file) => !file.endsWith("/"))
12330
12710
  .sort();
12331
- const { hasTests, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12711
+ const { hasTests, diagnostics: prevDiagnostics, ...prevOptions } = JSON.parse(await promises_namespaceObject.readFile(external_path_.join(output, "build-info.json"), "utf-8")
12332
12712
  .catch(() => "{}"));
12333
12713
  // check that the set of files thats actually there is the same as the
12334
12714
  // set of files we're going to generate (in case eg a jungle file change
@@ -12346,35 +12726,31 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12346
12726
  // the oldest optimized file, we don't need to regenerate
12347
12727
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12348
12728
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12349
- if (source_time < opt_time && 1654222986123 < opt_time) {
12350
- return hasTests;
12729
+ if (source_time < opt_time && 1654554780017 < opt_time) {
12730
+ return { hasTests, diagnostics: prevDiagnostics };
12351
12731
  }
12352
12732
  }
12353
12733
  await promises_namespaceObject.rm(output, { recursive: true, force: true });
12354
12734
  await promises_namespaceObject.mkdir(output, { recursive: true });
12355
- await optimizeMonkeyC(fnMap);
12735
+ const diagnostics = await optimizeMonkeyC(fnMap);
12356
12736
  return Promise.all(Object.values(fnMap).map(async (info) => {
12357
12737
  const name = info.output;
12358
12738
  const dir = external_path_.dirname(name);
12359
12739
  await promises_namespaceObject.mkdir(dir, { recursive: true });
12360
12740
  const opt_source = (0,external_api_cjs_namespaceObject.formatAst)(info.ast, info.monkeyCSource);
12741
+ await promises_namespaceObject.writeFile(name, opt_source);
12361
12742
  if (config.checkBuildPragmas) {
12362
- const matches = opt_source.matchAll(/^.*\/\*\s*@match\s+(\S+)\s+\*\/(.*)$/gm);
12363
- for (const [line, needle, haystack] of matches) {
12364
- if (!haystack.includes(needle)) {
12365
- throw new Error(`Checking build pragmas in ${name} failed at \n\n${line}\n\n - Didn't find '${needle}'`);
12366
- }
12367
- }
12743
+ pragmaChecker(info.ast);
12368
12744
  }
12369
- await promises_namespaceObject.writeFile(name, opt_source);
12370
12745
  return info.hasTests;
12371
12746
  })).then((results) => {
12372
12747
  const hasTests = results.some((v) => v);
12373
12748
  return promises_namespaceObject.writeFile(external_path_.join(output, "build-info.json"), JSON.stringify({
12374
12749
  hasTests,
12750
+ diagnostics,
12375
12751
  ...Object.fromEntries(configOptionsToCheck.map((option) => [option, config[option]])),
12376
12752
  }))
12377
- .then(() => hasTests);
12753
+ .then(() => ({ hasTests, diagnostics }));
12378
12754
  });
12379
12755
  }
12380
12756
  async function getProjectAnalysis(targets, analysis, options) {