@markw65/monkeyc-optimizer 1.0.14 → 1.0.17

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,479 @@ 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
+
10849
+ ;// CONCATENATED MODULE: ./src/inliner.ts
10850
+
10851
+
10852
+ function getArgSafety(state, func, args, requireAll) {
10853
+ // determine whether decl might be changed by a function call
10854
+ // or assignment during the evaluation of FunctionStateNode.
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")
10862
+ 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")
10869
+ return true;
10870
+ }
10871
+ }
10872
+ return null;
10873
+ };
10874
+ const safeArgs = [];
10875
+ let allSafe = true;
10876
+ if (!args.every((arg) => {
10877
+ switch (arg.type) {
10878
+ case "Literal":
10879
+ safeArgs.push(true);
10880
+ return true;
10881
+ case "Identifier":
10882
+ case "MemberExpression": {
10883
+ const [, results] = state.lookup(arg);
10884
+ if (!results || results.length !== 1) {
10885
+ safeArgs.push(null);
10886
+ return !requireAll;
10887
+ }
10888
+ const safety = getSafety(results[0]);
10889
+ safeArgs.push(safety);
10890
+ if (!safety) {
10891
+ allSafe = false;
10892
+ if (safety === null) {
10893
+ return !requireAll;
10894
+ }
10895
+ }
10896
+ return true;
10897
+ }
10898
+ }
10899
+ allSafe = false;
10900
+ safeArgs.push(null);
10901
+ return !requireAll;
10902
+ })) {
10903
+ return false;
10904
+ }
10905
+ if (allSafe)
10906
+ return true;
10907
+ let callSeen = false;
10908
+ let ok = true;
10909
+ const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
10910
+ const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
10911
+ // look for uses of "unsafe" args that occur after a call.
10912
+ // use post to do the checking, because arguments are evaluated
10913
+ // prior to the call, so eg "return f(x.y);" is fine, but
10914
+ // "return f()+x.y" is not.
10915
+ //
10916
+ // We also have to use a "pre" to ensure that child nodes are
10917
+ // visited in source order (otherwise we could visit x.y before f()
10918
+ // in the above example)
10919
+ (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
10920
+ return Object.entries(node)
10921
+ .filter((kv) => Array.isArray(kv[1])
10922
+ ? kv[1].length !== 0 && (0,external_api_cjs_namespaceObject.hasProperty)(kv[1][0], "type")
10923
+ : (0,external_api_cjs_namespaceObject.hasProperty)(kv[1], "type"))
10924
+ .sort(([, a], [, b]) => getLoc(a) - getLoc(b))
10925
+ .map(([key]) => key);
10926
+ }, (node) => {
10927
+ switch (node.type) {
10928
+ case "CallExpression":
10929
+ case "NewExpression":
10930
+ case "AssignmentExpression":
10931
+ case "UpdateExpression":
10932
+ callSeen = true;
10933
+ break;
10934
+ case "Identifier":
10935
+ if (callSeen &&
10936
+ (0,external_api_cjs_namespaceObject.hasProperty)(params, node.name) &&
10937
+ !safeArgs[params[node.name]]) {
10938
+ safeArgs[params[node.name]] = null;
10939
+ }
10940
+ }
10941
+ });
10942
+ return safeArgs;
10943
+ }
10944
+ function canInline(state, func, args) {
10945
+ const safeArgs = getArgSafety(state, func, args, true);
10946
+ if (safeArgs === true || safeArgs === false) {
10947
+ return safeArgs;
10948
+ }
10949
+ return safeArgs.every((arg) => arg !== null);
10950
+ }
10951
+ function inliningLooksUseful(func, node) {
10952
+ while (true) {
10953
+ if (node.type === "BinaryExpression" && node.operator === "as") {
10954
+ node = node.left;
10955
+ }
10956
+ else if (node.type === "UnaryExpression" && node.operator === " as") {
10957
+ node = node.argument;
10958
+ }
10959
+ else {
10960
+ break;
10961
+ }
10962
+ }
10963
+ if (node.type === "Literal")
10964
+ return true;
10965
+ if (node.type === "Identifier") {
10966
+ if (func.params.length === 1 &&
10967
+ (0,external_api_cjs_namespaceObject.variableDeclarationName)(func.params[0]) === node.name) {
10968
+ return 1;
10969
+ }
10970
+ return true;
10971
+ }
10972
+ return false;
10973
+ }
10974
+ var InlineStatus;
10975
+ (function (InlineStatus) {
10976
+ InlineStatus[InlineStatus["Never"] = 0] = "Never";
10977
+ InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
10978
+ InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
10979
+ })(InlineStatus || (InlineStatus = {}));
10980
+ function shouldInline(state, func, args) {
10981
+ let autoInline = false;
10982
+ let inlineAsExpression = false;
10983
+ if (func.node.body &&
10984
+ func.node.body.body.length === 1 &&
10985
+ func.node.body.body[0].type === "ReturnStatement" &&
10986
+ func.node.body.body[0].argument &&
10987
+ func.node.params.length === args.length) {
10988
+ inlineAsExpression = true;
10989
+ autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
10990
+ }
10991
+ if (autoInline === 1) {
10992
+ return InlineStatus.AsExpression;
10993
+ }
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;
11007
+ }
11008
+ return InlineStatus.Never;
11009
+ }
11010
+ 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
+ }
11021
+ const pre = state.pre;
11022
+ const post = state.post;
11023
+ try {
11024
+ state.pre = (node) => {
11025
+ node.start = call.start;
11026
+ node.end = call.end;
11027
+ node.loc = call.loc;
11028
+ if (node === insertedVariableDecls)
11029
+ return false;
11030
+ const result = pre(node, state);
11031
+ if (!insertedVariableDecls && node.type === "BlockStatement") {
11032
+ const locals = state.localsStack[state.localsStack.length - 1];
11033
+ const { map } = locals;
11034
+ if (!map)
11035
+ throw new Error("No local variable map!");
11036
+ const declarations = func.node.params
11037
+ .map((param, i) => {
11038
+ const paramName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(param);
11039
+ if (params[paramName] >= 0)
11040
+ return null;
11041
+ const name = renameVariable(state, locals, paramName) || paramName;
11042
+ return {
11043
+ type: "VariableDeclarator",
11044
+ id: { type: "Identifier", name },
11045
+ kind: "var",
11046
+ init: call.arguments[i],
11047
+ };
11048
+ })
11049
+ .filter((n) => n != null);
11050
+ insertedVariableDecls = {
11051
+ type: "VariableDeclaration",
11052
+ declarations,
11053
+ kind: "var",
11054
+ };
11055
+ node.body.unshift(insertedVariableDecls);
11056
+ }
11057
+ return result;
11058
+ };
11059
+ state.post = (node) => {
11060
+ let replacement = null;
11061
+ switch (node.type) {
11062
+ case "Identifier": {
11063
+ if (state.inType)
11064
+ break;
11065
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
11066
+ const ix = params[node.name];
11067
+ if (ix >= 0) {
11068
+ replacement = call.arguments[ix];
11069
+ }
11070
+ break;
11071
+ }
11072
+ replacement = fixNodeScope(state, node, func.stack);
11073
+ if (!replacement) {
11074
+ throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
11075
+ }
11076
+ break;
11077
+ }
11078
+ }
11079
+ return post(replacement || node, state) || replacement;
11080
+ };
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
+ }
11088
+ }
11089
+ throw ex;
11090
+ }
11091
+ finally {
11092
+ state.pre = pre;
11093
+ state.post = post;
11094
+ }
11095
+ }
11096
+ function unused(expression, top) {
11097
+ switch (expression.type) {
11098
+ case "Literal":
11099
+ return [];
11100
+ case "Identifier":
11101
+ return [];
11102
+ case "BinaryExpression":
11103
+ if (expression.operator === "as") {
11104
+ return unused(expression.left);
11105
+ }
11106
+ // fall through
11107
+ case "LogicalExpression":
11108
+ return unused(expression.left).concat(unused(expression.right));
11109
+ case "UnaryExpression":
11110
+ return unused(expression.argument);
11111
+ case "MemberExpression":
11112
+ return unused(expression.object).concat(unused(expression.property));
11113
+ }
11114
+ return top
11115
+ ? null
11116
+ : [
11117
+ {
11118
+ type: "ExpressionStatement",
11119
+ expression,
11120
+ },
11121
+ ];
11122
+ }
11123
+ function inlineWithArgs(state, func, call) {
11124
+ if (!func.node || !func.node.body) {
11125
+ return null;
11126
+ }
11127
+ 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;
11135
+ }
11136
+ const body = JSON.parse(JSON.stringify(func.node.body));
11137
+ processInlineBody(state, func, call, body, func.node.params.length ? false : true);
11138
+ if (retStmtCount) {
11139
+ const last = body.body[body.body.length - 1];
11140
+ if (last.type != "ReturnStatement") {
11141
+ throw new Error("ReturnStatement got lost!");
11142
+ }
11143
+ if (last.argument) {
11144
+ const side_exprs = unused(last.argument);
11145
+ body.body.splice(body.body.length - 1, 1, ...side_exprs);
11146
+ }
11147
+ else {
11148
+ --body.body.length;
11149
+ }
11150
+ }
11151
+ return body;
11152
+ }
11153
+ function inlineFunction(state, func, call, inlineStatus) {
11154
+ if (inlineStatus == InlineStatus.AsStatement) {
11155
+ return inlineWithArgs(state, func, call);
11156
+ }
11157
+ const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
11158
+ 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;
11160
+ }
11161
+ function applyTypeIfNeeded(node) {
11162
+ if ("enumType" in node && node.enumType) {
11163
+ node = {
11164
+ type: "BinaryExpression",
11165
+ operator: "as",
11166
+ left: node,
11167
+ right: { type: "TypeSpecList", ts: [node.enumType] },
11168
+ };
11169
+ }
11170
+ return node;
11171
+ }
11172
+ function fixNodeScope(state, lookupNode, nodeStack) {
11173
+ 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
+ }
11181
+ }
11182
+ }
11183
+ const [, original] = state.lookup(lookupNode, null, nodeStack);
11184
+ if (!original) {
11185
+ return null;
11186
+ }
11187
+ const [, current] = state.lookup(lookupNode);
11188
+ // For now, leave it alone if it already maps to the same thing.
11189
+ // With a bit more work, we could find the guaranteed shortest
11190
+ // reference, and then use this to optimize *all* symbols, not
11191
+ // just fix inlined ones.
11192
+ if (current &&
11193
+ current.length === original.length &&
11194
+ current.every((item, index) => item == original[index])) {
11195
+ return lookupNode;
11196
+ }
11197
+ const node = lookupNode.type === "Identifier"
11198
+ ? lookupNode
11199
+ : lookupNode.property;
11200
+ if (original.length === 1 && original[0].type === "EnumStringMember") {
11201
+ return applyTypeIfNeeded(original[0].init);
11202
+ }
11203
+ const prefixes = original.map((sn) => {
11204
+ if ((0,external_api_cjs_namespaceObject.isStateNode)(sn) && sn.fullName) {
11205
+ return sn.fullName;
11206
+ }
11207
+ return "";
11208
+ });
11209
+ if (prefixes.length &&
11210
+ prefixes[0].startsWith("$.") &&
11211
+ prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
11212
+ const prefix = prefixes[0].split(".").slice(0, -1).reverse();
11213
+ let found = false;
11214
+ return prefix.reduce((current, name) => {
11215
+ if (found)
11216
+ return current;
11217
+ const [, results] = state.lookup(current);
11218
+ if (results &&
11219
+ results.length === original.length &&
11220
+ results.every((result, i) => result === original[i])) {
11221
+ found = true;
11222
+ return current;
11223
+ }
11224
+ const object = typeof name === "string"
11225
+ ? {
11226
+ type: "Identifier",
11227
+ name,
11228
+ start: node.start,
11229
+ end: node.end,
11230
+ loc: node.loc,
11231
+ }
11232
+ : name;
11233
+ let root = null;
11234
+ let property = current;
11235
+ while (property.type !== "Identifier") {
11236
+ root = property;
11237
+ property = property.object;
11238
+ }
11239
+ const mb = {
11240
+ type: "MemberExpression",
11241
+ object,
11242
+ property,
11243
+ computed: false,
11244
+ start: node.start,
11245
+ end: node.end,
11246
+ loc: node.loc,
11247
+ };
11248
+ if (root) {
11249
+ root.object = mb;
11250
+ }
11251
+ else {
11252
+ current = mb;
11253
+ }
11254
+ return current;
11255
+ }, node);
11256
+ }
11257
+ return null;
11258
+ }
11259
+
10794
11260
  ;// CONCATENATED MODULE: ./src/mc-rewrite.ts
10795
11261
 
10796
11262
 
10797
11263
 
10798
11264
 
11265
+
11266
+
10799
11267
  function processImports(allImports, lookup) {
10800
11268
  allImports.forEach(({ node, stack }) => {
10801
11269
  const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
@@ -10804,11 +11272,24 @@ function processImports(allImports, lookup) {
10804
11272
  if (!parent.decls)
10805
11273
  parent.decls = {};
10806
11274
  const decls = parent.decls;
10807
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(parent.decls, name))
10808
- parent.decls[name] = [];
11275
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(decls, name))
11276
+ decls[name] = [];
10809
11277
  module.forEach((m) => {
10810
11278
  if ((0,external_api_cjs_namespaceObject.isStateNode)(m) && m.type == "ModuleDeclaration") {
10811
11279
  (0,external_util_cjs_namespaceObject.pushUnique)(decls[name], m);
11280
+ if (!parent.type_decls)
11281
+ parent.type_decls = {};
11282
+ const tdecls = parent.type_decls;
11283
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
11284
+ tdecls[name] = [];
11285
+ (0,external_util_cjs_namespaceObject.pushUnique)(tdecls[name], m);
11286
+ if (node.type == "ImportModule" && m.type_decls) {
11287
+ Object.entries(m.type_decls).forEach(([name, decls]) => {
11288
+ if (!(0,external_api_cjs_namespaceObject.hasProperty)(tdecls, name))
11289
+ tdecls[name] = [];
11290
+ decls.forEach((decl) => (0,external_util_cjs_namespaceObject.pushUnique)(tdecls[name], decl));
11291
+ });
11292
+ }
10812
11293
  }
10813
11294
  });
10814
11295
  }
@@ -10817,11 +11298,39 @@ function processImports(allImports, lookup) {
10817
11298
  function collectClassInfo(state) {
10818
11299
  state.allClasses.forEach((elm) => {
10819
11300
  if (elm.node.superClass) {
10820
- const [, classes] = state.lookup(elm.node.superClass, null, elm.stack);
11301
+ const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
10821
11302
  const superClass = classes &&
10822
11303
  classes.filter((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && c.type === "ClassDeclaration");
10823
11304
  // set it "true" if there is a superClass, but we can't find it.
10824
11305
  elm.superClass = superClass && superClass.length ? superClass : true;
11306
+ if (name && elm.superClass !== true) {
11307
+ /*
11308
+ * The runtime behavior of monkeyc is strange. Lookups
11309
+ * of the name of the superclass, either bare, or via self.<name>
11310
+ * always find the superclass, even if there's a member variable
11311
+ * or method of the same name. So its ok to just overwrite
11312
+ * elm.decls[name] here.
11313
+ *
11314
+ * ie
11315
+ *
11316
+ * class A { function foo() as Number { return 1; } }
11317
+ * class B { function foo() as String { return "B"; } }
11318
+ * class C extends A {
11319
+ * var A as B = new B();
11320
+ * function initialize() {
11321
+ * A.initialize(); // class A's initialize
11322
+ * A.foo(); // returns 1
11323
+ * self.A.foo(); // still returns 1
11324
+ * }
11325
+ * }
11326
+ *
11327
+ * The typechecker seems to get confused in some circumstances
11328
+ * though (ie it doesn't always use the same rules)
11329
+ */
11330
+ if (!elm.decls)
11331
+ elm.decls = {};
11332
+ elm.decls[name] = elm.superClass;
11333
+ }
10825
11334
  }
10826
11335
  });
10827
11336
  const markOverrides = (cls, scls) => {
@@ -10876,30 +11385,34 @@ function getFileASTs(fnMap) {
10876
11385
  }, true));
10877
11386
  }
10878
11387
  async function analyze(fnMap) {
10879
- let excludeAnnotations;
10880
11388
  let hasTests = false;
10881
11389
  const allImports = [];
10882
11390
  const preState = {
11391
+ fnMap,
10883
11392
  allFunctions: [],
10884
11393
  allClasses: [],
10885
11394
  shouldExclude(node) {
10886
11395
  if ("attrs" in node &&
10887
11396
  node.attrs &&
10888
11397
  "attrs" in node.attrs &&
10889
- node.attrs.attrs) {
10890
- return node.attrs.attrs.reduce((drop, attr) => {
10891
- if (attr.type != "UnaryExpression")
10892
- return drop;
10893
- if (attr.argument.type != "Identifier")
11398
+ node.attrs.attrs &&
11399
+ node.loc?.source) {
11400
+ const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
11401
+ if (excludeAnnotations) {
11402
+ return node.attrs.attrs.reduce((drop, attr) => {
11403
+ if (attr.type != "UnaryExpression")
11404
+ return drop;
11405
+ if (attr.argument.type != "Identifier")
11406
+ return drop;
11407
+ if ((0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name)) {
11408
+ return true;
11409
+ }
11410
+ if (attr.argument.name == "test") {
11411
+ hasTests = true;
11412
+ }
10894
11413
  return drop;
10895
- if ((0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name)) {
10896
- return true;
10897
- }
10898
- if (attr.argument.name == "test") {
10899
- hasTests = true;
10900
- }
10901
- return drop;
10902
- }, false);
11414
+ }, false);
11415
+ }
10903
11416
  }
10904
11417
  return false;
10905
11418
  },
@@ -10948,7 +11461,6 @@ async function analyze(fnMap) {
10948
11461
  if (!ast) {
10949
11462
  throw parserError || new Error(`Failed to parse ${name}`);
10950
11463
  }
10951
- excludeAnnotations = value.excludeAnnotations;
10952
11464
  hasTests = false;
10953
11465
  (0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
10954
11466
  value.hasTests = hasTests;
@@ -10972,8 +11484,8 @@ function getLiteralFromDecls(decls) {
10972
11484
  let result = null;
10973
11485
  if (decls.every((d) => {
10974
11486
  if (d.type === "EnumStringMember" ||
10975
- (d.type === "VariableDeclarator" && d.kind === "const")) {
10976
- const init = getLiteralNode(d.init);
11487
+ (d.type === "VariableDeclarator" && d.node.kind === "const")) {
11488
+ const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
10977
11489
  if (!init)
10978
11490
  return false;
10979
11491
  if (!result) {
@@ -11255,11 +11767,12 @@ async function optimizeMonkeyC(fnMap) {
11255
11767
  * anywhere in its superClass chain.
11256
11768
  */
11257
11769
  const checkInherited = (elm, name) => elm.superClass === true ||
11258
- elm.superClass.some((sc) => ((0,external_api_cjs_namespaceObject.hasProperty)(sc.decls, name) &&
11259
- sc.decls[name].some((f) => (0,external_api_cjs_namespaceObject.isStateNode)(f) &&
11260
- f.type == "FunctionDeclaration" &&
11261
- maybeCalled(f.node))) ||
11262
- (sc.superClass && checkInherited(sc, name)));
11770
+ (elm.superClass != null &&
11771
+ elm.superClass.some((sc) => ((0,external_api_cjs_namespaceObject.hasProperty)(sc.decls, name) &&
11772
+ sc.decls[name].some((f) => (0,external_api_cjs_namespaceObject.isStateNode)(f) &&
11773
+ f.type == "FunctionDeclaration" &&
11774
+ maybeCalled(f.node))) ||
11775
+ (sc.superClass && checkInherited(sc, name))));
11263
11776
  state.pre = (node) => {
11264
11777
  switch (node.type) {
11265
11778
  case "ConditionalExpression":
@@ -11311,57 +11824,8 @@ async function optimizeMonkeyC(fnMap) {
11311
11824
  const { map } = locals;
11312
11825
  if (map) {
11313
11826
  const declName = (0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id);
11314
- if ((0,external_api_cjs_namespaceObject.hasProperty)(map, declName)) {
11315
- // We already have a variable with this name in scope
11316
- // Recent monkeyc compilers complain, so rename it
11317
- let suffix = 0;
11318
- let node_name = declName;
11319
- const match = node_name.match(/^pmcr_(.*)_(\d+)$/);
11320
- if (match) {
11321
- node_name = match[1];
11322
- suffix = parseInt(match[2], 10) + 1;
11323
- }
11324
- if (!locals.inners) {
11325
- // find all the names declared in this scope, to avoid
11326
- // more conflicts
11327
- locals.inners = {};
11328
- const inners = locals.inners;
11329
- (0,external_api_cjs_namespaceObject.traverseAst)(locals.node, (node) => {
11330
- if (node.type === "VariableDeclarator") {
11331
- inners[(0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id)] = true;
11332
- }
11333
- });
11334
- }
11335
- let name;
11336
- while (true) {
11337
- name = `pmcr_${node_name}_${suffix}`;
11338
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(map, name) &&
11339
- !(0,external_api_cjs_namespaceObject.hasProperty)(locals.inners, name)) {
11340
- // we also need to ensure that we don't hide the name of
11341
- // an outer module, class, function, enum or variable,
11342
- // since someone might want to access it from this scope.
11343
- let ok = false;
11344
- let i;
11345
- for (i = state.stack.length; i--;) {
11346
- const elm = state.stack[i];
11347
- if (ok) {
11348
- if ((0,external_api_cjs_namespaceObject.hasProperty)(elm.decls, name)) {
11349
- break;
11350
- }
11351
- }
11352
- else if (elm.node &&
11353
- elm.node.type === "FunctionDeclaration") {
11354
- ok = true;
11355
- }
11356
- }
11357
- if (i < 0) {
11358
- break;
11359
- }
11360
- }
11361
- suffix++;
11362
- }
11363
- map[declName] = name;
11364
- map[name] = true;
11827
+ const name = renameVariable(state, locals, declName);
11828
+ if (name) {
11365
11829
  if (node.id.type === "Identifier") {
11366
11830
  node.id.name = name;
11367
11831
  }
@@ -11385,7 +11849,7 @@ async function optimizeMonkeyC(fnMap) {
11385
11849
  state.exposed[node.argument.name] = true;
11386
11850
  // In any case, we can't replace *this* use of the
11387
11851
  // symbol with its value...
11388
- return false;
11852
+ return [];
11389
11853
  }
11390
11854
  break;
11391
11855
  case "Identifier": {
@@ -11403,7 +11867,7 @@ async function optimizeMonkeyC(fnMap) {
11403
11867
  state.exposed[node.name] = true;
11404
11868
  }
11405
11869
  }
11406
- return false;
11870
+ return [];
11407
11871
  }
11408
11872
  case "MemberExpression":
11409
11873
  if (node.property.type === "Identifier" && !node.computed) {
@@ -11485,49 +11949,37 @@ async function optimizeMonkeyC(fnMap) {
11485
11949
  }
11486
11950
  break;
11487
11951
  case "CallExpression": {
11488
- const [name, callees] = state.lookup(node.callee);
11489
- if (!callees || !callees.length) {
11490
- const n = name ||
11491
- ("name" in node.callee && node.callee.name) ||
11492
- ("property" in node.callee &&
11493
- node.callee.property &&
11494
- "name" in node.callee.property &&
11495
- node.callee.property.name);
11496
- if (n) {
11497
- state.exposed[n] = true;
11498
- }
11499
- else {
11500
- // There are unnamed CallExpressions, such as new [size]
11501
- // So there's nothing to do here.
11502
- }
11503
- return null;
11952
+ const ret = optimizeCall(state, node, false);
11953
+ if (ret) {
11954
+ replace(node, ret);
11504
11955
  }
11505
- if (callees.length == 1) {
11506
- const callee = (0,external_api_cjs_namespaceObject.isStateNode)(callees[0]) && callees[0].node;
11507
- if (callee &&
11508
- callee.type == "FunctionDeclaration" &&
11509
- callee.optimizable &&
11510
- !callee.hasOverride &&
11511
- node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
11512
- const ret = evaluateFunction(callee, node.arguments);
11513
- if (ret) {
11514
- replace(node, ret);
11515
- return null;
11956
+ break;
11957
+ }
11958
+ case "ExpressionStatement":
11959
+ if (node.expression.type === "CallExpression") {
11960
+ const ret = optimizeCall(state, node.expression, true);
11961
+ if (ret) {
11962
+ if (ret.type === "BlockStatement") {
11963
+ return ret;
11516
11964
  }
11965
+ node.expression = ret;
11517
11966
  }
11518
11967
  }
11519
- if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
11520
- state.calledFunctions[name] = [];
11968
+ else {
11969
+ const ret = unused(node.expression, true);
11970
+ if (ret) {
11971
+ return ret;
11972
+ }
11521
11973
  }
11522
- callees.forEach((c) => (0,external_api_cjs_namespaceObject.isStateNode)(c) && state.calledFunctions[name].push(c.node));
11523
11974
  break;
11524
- }
11525
11975
  }
11526
11976
  return null;
11527
11977
  };
11528
11978
  Object.values(fnMap).forEach((f) => {
11529
11979
  (0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
11530
11980
  });
11981
+ delete state.pre;
11982
+ delete state.post;
11531
11983
  const cleanup = (node) => {
11532
11984
  switch (node.type) {
11533
11985
  case "EnumStringBody":
@@ -11602,6 +12054,98 @@ async function optimizeMonkeyC(fnMap) {
11602
12054
  });
11603
12055
  });
11604
12056
  }
12057
+ function optimizeCall(state, node, asStatement) {
12058
+ const [name, callees] = state.lookup(node.callee);
12059
+ if (!callees || !callees.length) {
12060
+ const n = name ||
12061
+ ("name" in node.callee && node.callee.name) ||
12062
+ ("property" in node.callee &&
12063
+ node.callee.property &&
12064
+ "name" in node.callee.property &&
12065
+ node.callee.property.name);
12066
+ if (n) {
12067
+ state.exposed[n] = true;
12068
+ }
12069
+ else {
12070
+ // There are unnamed CallExpressions, such as new [size]
12071
+ // So there's nothing to do here.
12072
+ }
12073
+ return null;
12074
+ }
12075
+ if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
12076
+ const callee = callees[0].node;
12077
+ if (callee.optimizable &&
12078
+ !callee.hasOverride &&
12079
+ node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
12080
+ const ret = evaluateFunction(callee, node.arguments);
12081
+ if (ret) {
12082
+ return ret;
12083
+ }
12084
+ }
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);
12089
+ if (ret) {
12090
+ return ret;
12091
+ }
12092
+ }
12093
+ }
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));
12098
+ return null;
12099
+ }
12100
+
12101
+ ;// CONCATENATED MODULE: ./src/pragma-checker.ts
12102
+
12103
+ function pragmaChecker(ast) {
12104
+ const comments = ast.comments;
12105
+ if (!comments)
12106
+ return;
12107
+ let index = -1;
12108
+ let comment;
12109
+ const next = () => {
12110
+ while (++index < comments.length) {
12111
+ comment = comments[index];
12112
+ if (comment.value.match(/^\s*@match\s+/)) {
12113
+ break;
12114
+ }
12115
+ }
12116
+ };
12117
+ next();
12118
+ (0,external_api_cjs_namespaceObject.traverseAst)(ast, (node) => {
12119
+ if (index >= comments.length)
12120
+ return false;
12121
+ 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
+ }
12126
+ const haystack = (0,external_api_cjs_namespaceObject.formatAst)(node).replace(/[\r\n]/g, " ");
12127
+ let found = false;
12128
+ let needle = match[1];
12129
+ if (match.length == 2) {
12130
+ found = haystack.includes(needle);
12131
+ }
12132
+ 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
+ }
12140
+ }
12141
+ if (!found) {
12142
+ throw new Error(`Didn't find '${needle}' in ${comment.loc.source}:${comment.loc.start.line}`);
12143
+ }
12144
+ next();
12145
+ }
12146
+ return null;
12147
+ });
12148
+ }
11605
12149
 
11606
12150
  ;// CONCATENATED MODULE: ./src/optimizer.ts
11607
12151
 
@@ -11617,6 +12161,7 @@ async function optimizeMonkeyC(fnMap) {
11617
12161
 
11618
12162
 
11619
12163
 
12164
+
11620
12165
  function relative_path_no_dotdot(relative) {
11621
12166
  return relative.replace(/^(\.\.[\\\/])+/, (str) => `__${"dot".repeat(str.length / 3)}__${str.slice(-1)}`);
11622
12167
  }
@@ -12015,6 +12560,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12015
12560
  // might have altered it), and that the options we care about haven't
12016
12561
  // changed
12017
12562
  if (hasTests != null &&
12563
+ !config.checkBuildPragmas &&
12018
12564
  configOptionsToCheck.every((option) => prevOptions[option] === config[option]) &&
12019
12565
  actualOptimizedFiles.length == Object.values(fnMap).length &&
12020
12566
  Object.values(fnMap)
@@ -12025,7 +12571,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12025
12571
  // the oldest optimized file, we don't need to regenerate
12026
12572
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12027
12573
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12028
- if (source_time < opt_time && 1653864408816 < opt_time) {
12574
+ if (source_time < opt_time && 1654384985724 < opt_time) {
12029
12575
  return hasTests;
12030
12576
  }
12031
12577
  }
@@ -12038,6 +12584,9 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12038
12584
  await promises_namespaceObject.mkdir(dir, { recursive: true });
12039
12585
  const opt_source = (0,external_api_cjs_namespaceObject.formatAst)(info.ast, info.monkeyCSource);
12040
12586
  await promises_namespaceObject.writeFile(name, opt_source);
12587
+ if (config.checkBuildPragmas) {
12588
+ pragmaChecker(info.ast);
12589
+ }
12041
12590
  return info.hasTests;
12042
12591
  })).then((results) => {
12043
12592
  const hasTests = results.some((v) => v);
@@ -12094,11 +12643,12 @@ async function generateApiMirTests(options) {
12094
12643
  return;
12095
12644
  const d = decl[0];
12096
12645
  if (d.type === "EnumStringMember" ||
12097
- (d.type === "VariableDeclarator" && d.kind === "const")) {
12098
- if (!d.init) {
12646
+ (d.type === "VariableDeclarator" && d.node.kind === "const")) {
12647
+ const init = (0,external_api_cjs_namespaceObject.isStateNode)(d) ? d.node.init : d.init;
12648
+ if (!init) {
12099
12649
  throw new Error(`Missing init for ${node.fullName}.${key}`);
12100
12650
  }
12101
- tests.push([`${node.fullName}.${key}`, (0,external_api_cjs_namespaceObject.formatAst)(d.init)]);
12651
+ tests.push([`${node.fullName}.${key}`, (0,external_api_cjs_namespaceObject.formatAst)(init)]);
12102
12652
  }
12103
12653
  else if ((0,external_api_cjs_namespaceObject.isStateNode)(d)) {
12104
12654
  findConstants(d);