@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.
package/build/api.cjs CHANGED
@@ -132,23 +132,32 @@ function getArgSafety(state, func, args, requireAll) {
132
132
  // determine whether decl might be changed by a function call
133
133
  // or assignment during the evaluation of FunctionStateNode.
134
134
  const getSafety = (decl) => {
135
- // enums are constant, they cant change
136
- if (decl.type === "EnumStringMember")
137
- return true;
138
- if (decl.type === "VariableDeclarator") {
139
- // constants also can't change
140
- if (decl.node.kind === "const")
135
+ switch (decl.type) {
136
+ // enums are constant, they cant change
137
+ case "EnumStringMember":
141
138
  return true;
142
- // if decl is a local, it also can't be changed
143
- // by a call to another function.
144
- for (let i = 0;; i++) {
145
- if (!state.stack[i] || decl.stack[i] !== state.stack[i])
146
- return false;
147
- if (state.stack[i].type === "FunctionDeclaration")
139
+ case "VariableDeclarator": {
140
+ // constants also can't change
141
+ if (decl.node.kind === "const")
148
142
  return true;
143
+ // if decl is a local, it also can't be changed
144
+ // by a call to another function.
145
+ for (let i = 0;; i++) {
146
+ if (!state.stack[i] || decl.stack[i] !== state.stack[i])
147
+ return false;
148
+ if (state.stack[i].type === "FunctionDeclaration")
149
+ return true;
150
+ }
149
151
  }
152
+ case "Identifier":
153
+ case "BinaryExpression":
154
+ // This is a parameter of the calling function.
155
+ // It also can't be changed during the execution
156
+ // of the inlined function
157
+ return true;
158
+ default:
159
+ return null;
150
160
  }
151
- return null;
152
161
  };
153
162
  const safeArgs = [];
154
163
  let allSafe = true;
@@ -256,15 +265,32 @@ function inliningLooksUseful(func, node) {
256
265
  }
257
266
  return false;
258
267
  }
259
- var inliner_InlineStatus;
268
+ var InlineStatus;
260
269
  (function (InlineStatus) {
261
270
  InlineStatus[InlineStatus["Never"] = 0] = "Never";
262
271
  InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
263
272
  InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
264
- })(inliner_InlineStatus || (inliner_InlineStatus = {}));
265
- function inliner_shouldInline(state, func, args) {
273
+ })(InlineStatus || (InlineStatus = {}));
274
+ function inlineRequested(state, func) {
275
+ const excludeAnnotations = (func.node.loc?.source &&
276
+ state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
277
+ {};
278
+ if (func.node.attrs &&
279
+ func.node.attrs.attributes &&
280
+ func.node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" &&
281
+ (attr.argument.name === "inline" ||
282
+ (attr.argument.name.startsWith("inline_") &&
283
+ hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
284
+ return true;
285
+ }
286
+ return false;
287
+ }
288
+ function inliner_shouldInline(state, func, call, context) {
289
+ if (state.inlining)
290
+ return false;
266
291
  let autoInline = false;
267
292
  let inlineAsExpression = false;
293
+ const args = call.arguments;
268
294
  if (func.node.body &&
269
295
  func.node.body.body.length === 1 &&
270
296
  func.node.body.body[0].type === "ReturnStatement" &&
@@ -274,39 +300,31 @@ function inliner_shouldInline(state, func, args) {
274
300
  autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
275
301
  }
276
302
  if (autoInline === 1) {
277
- return inliner_InlineStatus.AsExpression;
303
+ return true;
278
304
  }
279
- const excludeAnnotations = (func.node.loc?.source &&
280
- state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
281
- {};
282
- const inlineRequested = func.node.attrs &&
283
- func.node.attrs.attrs &&
284
- func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
285
- (attr.argument.name === "inline" ||
286
- (attr.argument.name.startsWith("inline_") &&
287
- hasProperty(excludeAnnotations, attr.argument.name.substring(7)))));
288
- if (autoInline || inlineRequested) {
289
- return inlineAsExpression && canInline(state, func, args)
290
- ? inliner_InlineStatus.AsExpression
291
- : inliner_InlineStatus.AsStatement;
305
+ const requested = inlineRequested(state, func);
306
+ if (autoInline || requested) {
307
+ if (inlineAsExpression) {
308
+ if (canInline(state, func, args)) {
309
+ return true;
310
+ }
311
+ }
312
+ if (!context && requested) {
313
+ inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, or return contexts");
314
+ }
315
+ return context != null;
292
316
  }
293
- return inliner_InlineStatus.Never;
317
+ return false;
294
318
  }
295
319
  function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
296
- if (!params) {
297
- const safeArgs = getArgSafety(state, func, call.arguments, false);
298
- params = Object.fromEntries(func.node.params.map((param, i) => {
299
- const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
300
- ? i
301
- : -1;
302
- const name = variableDeclarationName(param);
303
- return [name, argnum];
304
- }));
305
- }
320
+ let failed = false;
306
321
  const pre = state.pre;
307
322
  const post = state.post;
323
+ state.inlining = true;
308
324
  try {
309
325
  state.pre = (node) => {
326
+ if (failed)
327
+ return [];
310
328
  node.start = call.start;
311
329
  node.end = call.end;
312
330
  node.loc = call.loc;
@@ -318,6 +336,11 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
318
336
  const { map } = locals;
319
337
  if (!map)
320
338
  throw new Error("No local variable map!");
339
+ // We still need to keep track of every local name that was
340
+ // already in use, but we don't want to use any of its renames.
341
+ // We also want to know whether a local is from the function being
342
+ // inlined, or the calling function, so set every element to false.
343
+ Object.keys(map).forEach((key) => (map[key] = false));
321
344
  const declarations = func.node.params
322
345
  .map((param, i) => {
323
346
  const paramName = variableDeclarationName(param);
@@ -342,6 +365,8 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
342
365
  return result;
343
366
  };
344
367
  state.post = (node) => {
368
+ if (failed)
369
+ return post(node, state);
345
370
  let replacement = null;
346
371
  switch (node.type) {
347
372
  case "Identifier": {
@@ -356,26 +381,34 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
356
381
  }
357
382
  replacement = fixNodeScope(state, node, func.stack);
358
383
  if (!replacement) {
359
- throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
384
+ failed = true;
385
+ inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
386
+ return post(node, state);
360
387
  }
361
388
  break;
362
389
  }
363
390
  }
364
- return post(replacement || node, state) || replacement;
391
+ const ret = post(replacement || node, state);
392
+ return ret === false || ret ? ret : replacement;
365
393
  };
366
- return state.traverse(root) || null;
367
- }
368
- catch (ex) {
369
- if (ex instanceof Error) {
370
- if (ex.message.startsWith("Inliner: ")) {
371
- return null;
372
- }
394
+ let ret = state.traverse(root);
395
+ if (failed) {
396
+ return null;
373
397
  }
374
- throw ex;
398
+ if (ret === null) {
399
+ ret = root;
400
+ }
401
+ if (!ret) {
402
+ inlineDiagnostic(state, func, call, `Internal error`);
403
+ return null;
404
+ }
405
+ inlineDiagnostic(state, func, call, null);
406
+ return ret;
375
407
  }
376
408
  finally {
377
409
  state.pre = pre;
378
410
  state.post = post;
411
+ delete state.inlining;
379
412
  }
380
413
  }
381
414
  function inliner_unused(expression, top) {
@@ -394,7 +427,16 @@ function inliner_unused(expression, top) {
394
427
  case "UnaryExpression":
395
428
  return inliner_unused(expression.argument);
396
429
  case "MemberExpression":
397
- return inliner_unused(expression.object).concat(inliner_unused(expression.property));
430
+ if (expression.computed) {
431
+ return inliner_unused(expression.object).concat(inliner_unused(expression.property));
432
+ }
433
+ return inliner_unused(expression.object);
434
+ case "ArrayExpression":
435
+ return expression.elements.map((e) => inliner_unused(e)).flat(1);
436
+ case "ObjectExpression":
437
+ return expression.properties
438
+ .map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
439
+ .flat(1);
398
440
  }
399
441
  return top
400
442
  ? null
@@ -402,30 +444,98 @@ function inliner_unused(expression, top) {
402
444
  {
403
445
  type: "ExpressionStatement",
404
446
  expression,
447
+ start: expression.start,
448
+ end: expression.end,
449
+ loc: expression.loc,
405
450
  },
406
451
  ];
407
452
  }
408
- function inlineWithArgs(state, func, call) {
453
+ function diagnostic(state, loc, message) {
454
+ if (!loc || !loc.source)
455
+ return;
456
+ const source = loc.source;
457
+ if (!state.diagnostics)
458
+ state.diagnostics = {};
459
+ if (!hasProperty(state.diagnostics, source)) {
460
+ if (!message)
461
+ return;
462
+ state.diagnostics[source] = [];
463
+ }
464
+ const diags = state.diagnostics[source];
465
+ let index = diags.findIndex((item) => item.loc === loc);
466
+ if (message) {
467
+ if (index < 0)
468
+ index = diags.length;
469
+ diags[index] = { type: "INFO", loc, message };
470
+ }
471
+ else if (index >= 0) {
472
+ diags.splice(index, 1);
473
+ }
474
+ }
475
+ function inlineDiagnostic(state, func, call, message) {
476
+ if (inlineRequested(state, func)) {
477
+ diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
478
+ }
479
+ }
480
+ function inlineWithArgs(state, func, call, context) {
409
481
  if (!func.node || !func.node.body) {
410
482
  return null;
411
483
  }
412
484
  let retStmtCount = 0;
413
- traverseAst(func.node.body, (node) => {
414
- node.type === "ReturnStatement" && retStmtCount++;
415
- });
416
- if (retStmtCount > 1 ||
417
- (retStmtCount === 1 &&
418
- func.node.body.body.slice(-1)[0].type !== "ReturnStatement")) {
419
- return null;
485
+ if (context.type === "ReturnStatement") {
486
+ const last = func.node.body.body.slice(-1)[0];
487
+ if (!last || last.type !== "ReturnStatement") {
488
+ inlineDiagnostic(state, func, call, "Function didn't end with a return statement");
489
+ return null;
490
+ }
491
+ }
492
+ else {
493
+ traverseAst(func.node.body, (node) => {
494
+ node.type === "ReturnStatement" && retStmtCount++;
495
+ });
496
+ if (retStmtCount > 1) {
497
+ inlineDiagnostic(state, func, call, "Function had more than one return statement");
498
+ }
499
+ else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
500
+ inlineDiagnostic(state, func, call, "Function did not have a return statement");
501
+ return null;
502
+ }
503
+ if (retStmtCount === 1) {
504
+ const last = func.node.body.body.slice(-1)[0];
505
+ if (!last ||
506
+ last.type !== "ReturnStatement" ||
507
+ (context.type === "AssignmentExpression" && !last.argument)) {
508
+ inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
509
+ return null;
510
+ }
511
+ }
420
512
  }
421
513
  const body = JSON.parse(JSON.stringify(func.node.body));
422
- processInlineBody(state, func, call, body, func.node.params.length ? false : true);
423
- if (retStmtCount) {
514
+ const safeArgs = getArgSafety(state, func, call.arguments, false);
515
+ const params = Object.fromEntries(func.node.params.map((param, i) => {
516
+ const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
517
+ ? i
518
+ : -1;
519
+ const name = variableDeclarationName(param);
520
+ return [name, argnum];
521
+ }));
522
+ if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
523
+ return null;
524
+ }
525
+ diagnostic(state, call.loc, null);
526
+ if (context.type !== "ReturnStatement" && retStmtCount) {
424
527
  const last = body.body[body.body.length - 1];
425
528
  if (last.type != "ReturnStatement") {
426
529
  throw new Error("ReturnStatement got lost!");
427
530
  }
428
- if (last.argument) {
531
+ if (context.type === "AssignmentExpression") {
532
+ context.right = last.argument;
533
+ body.body[body.body.length - 1] = {
534
+ type: "ExpressionStatement",
535
+ expression: context,
536
+ };
537
+ }
538
+ else if (last.argument) {
429
539
  const side_exprs = inliner_unused(last.argument);
430
540
  body.body.splice(body.body.length - 1, 1, ...side_exprs);
431
541
  }
@@ -435,13 +545,13 @@ function inlineWithArgs(state, func, call) {
435
545
  }
436
546
  return body;
437
547
  }
438
- function inliner_inlineFunction(state, func, call, inlineStatus) {
439
- if (inlineStatus == inliner_InlineStatus.AsStatement) {
440
- return inlineWithArgs(state, func, call);
548
+ function inliner_inlineFunction(state, func, call, context) {
549
+ if (context) {
550
+ return inlineWithArgs(state, func, call, context);
441
551
  }
442
552
  const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
443
553
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
444
- return processInlineBody(state, func, call, retArg, true, params) || retArg;
554
+ return processInlineBody(state, func, call, retArg, true, params);
445
555
  }
446
556
  function applyTypeIfNeeded(node) {
447
557
  if ("enumType" in node && node.enumType) {
@@ -456,13 +566,14 @@ function applyTypeIfNeeded(node) {
456
566
  }
457
567
  function fixNodeScope(state, lookupNode, nodeStack) {
458
568
  if (lookupNode.type === "Identifier") {
459
- for (let i = state.stack.length; --i > nodeStack.length;) {
460
- const si = state.stack[i];
461
- if (hasProperty(si.decls, lookupNode.name)) {
462
- // its a local from the inlined function.
463
- // Nothing to do.
464
- return lookupNode;
465
- }
569
+ const locals = state.localsStack[state.localsStack.length - 1];
570
+ const { map } = locals;
571
+ if (!map)
572
+ throw new Error("No local variable map!");
573
+ if (hasProperty(map, lookupNode.name) && map[lookupNode.name] !== false) {
574
+ // map[name] !== false means its an entry that was created during inlining
575
+ // so its definitely one of our locals.
576
+ return lookupNode;
466
577
  }
467
578
  }
468
579
  const [, original] = state.lookup(lookupNode, null, nodeStack);
@@ -682,12 +793,12 @@ async function analyze(fnMap) {
682
793
  shouldExclude(node) {
683
794
  if ("attrs" in node &&
684
795
  node.attrs &&
685
- "attrs" in node.attrs &&
686
- node.attrs.attrs &&
796
+ "attributes" in node.attrs &&
797
+ node.attrs.attributes &&
687
798
  node.loc?.source) {
688
799
  const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
689
800
  if (excludeAnnotations) {
690
- return node.attrs.attrs.reduce((drop, attr) => {
801
+ return node.attrs.attributes.elements.reduce((drop, attr) => {
691
802
  if (attr.type != "UnaryExpression")
692
803
  return drop;
693
804
  if (attr.argument.type != "Identifier")
@@ -938,13 +1049,15 @@ function optimizeNode(node) {
938
1049
  return null;
939
1050
  }
940
1051
  function evaluateFunction(func, args) {
941
- if (args && args.length != func.params.length) {
1052
+ if (!func.body || (args && args.length != func.params.length)) {
942
1053
  return false;
943
1054
  }
944
1055
  const paramValues = args &&
945
1056
  Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
946
1057
  let ret = null;
947
- const body = args ? JSON.parse(JSON.stringify(func.body)) : func.body;
1058
+ const body = args
1059
+ ? JSON.parse(JSON.stringify(func.body))
1060
+ : func.body;
948
1061
  try {
949
1062
  traverseAst(body, (node) => {
950
1063
  switch (node.type) {
@@ -987,6 +1100,13 @@ function evaluateFunction(func, args) {
987
1100
  return false;
988
1101
  }
989
1102
  }
1103
+ function markFunctionCalled(state, func) {
1104
+ if (!hasProperty(state.calledFunctions, func.id.name)) {
1105
+ state.calledFunctions[func.id.name] = [func];
1106
+ return;
1107
+ }
1108
+ pushUnique(state.calledFunctions[func.id.name], func);
1109
+ }
990
1110
  async function optimizeMonkeyC(fnMap) {
991
1111
  const state = {
992
1112
  ...(await analyze(fnMap)),
@@ -994,7 +1114,13 @@ async function optimizeMonkeyC(fnMap) {
994
1114
  exposed: {},
995
1115
  calledFunctions: {},
996
1116
  };
997
- const replace = (node, obj) => {
1117
+ const replace = (node) => {
1118
+ if (node === false || node === null)
1119
+ return node;
1120
+ const rep = state.traverse(node);
1121
+ return rep === false || rep ? rep : node;
1122
+ };
1123
+ const inPlaceReplacement = (node, obj) => {
998
1124
  for (const k of Object.keys(node)) {
999
1125
  delete node[k];
1000
1126
  }
@@ -1019,7 +1145,7 @@ async function optimizeMonkeyC(fnMap) {
1019
1145
  if (!obj) {
1020
1146
  return false;
1021
1147
  }
1022
- replace(node, obj);
1148
+ inPlaceReplacement(node, obj);
1023
1149
  return true;
1024
1150
  };
1025
1151
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
@@ -1035,8 +1161,8 @@ async function optimizeMonkeyC(fnMap) {
1035
1161
  if (hasProperty(state.exposed, func.id.name))
1036
1162
  return true;
1037
1163
  if (func.attrs &&
1038
- func.attrs.attrs &&
1039
- func.attrs.attrs.some((attr) => {
1164
+ func.attrs.attributes &&
1165
+ func.attrs.attributes.elements.some((attr) => {
1040
1166
  if (attr.type != "UnaryExpression")
1041
1167
  return false;
1042
1168
  if (attr.argument.type != "Identifier")
@@ -1145,7 +1271,7 @@ async function optimizeMonkeyC(fnMap) {
1145
1271
  if (map) {
1146
1272
  if (hasProperty(map, node.name)) {
1147
1273
  const name = map[node.name];
1148
- if (name !== true) {
1274
+ if (typeof name === "string") {
1149
1275
  node.name = name;
1150
1276
  }
1151
1277
  }
@@ -1196,10 +1322,7 @@ async function optimizeMonkeyC(fnMap) {
1196
1322
  used = checkInherited(parent, node.id.name);
1197
1323
  }
1198
1324
  if (used) {
1199
- if (!hasProperty(state.calledFunctions, node.id.name)) {
1200
- state.calledFunctions[node.id.name] = [];
1201
- }
1202
- state.calledFunctions[node.id.name].push(node);
1325
+ markFunctionCalled(state, node);
1203
1326
  }
1204
1327
  }
1205
1328
  }
@@ -1212,8 +1335,7 @@ async function optimizeMonkeyC(fnMap) {
1212
1335
  }
1213
1336
  const opt = optimizeNode(node);
1214
1337
  if (opt) {
1215
- replace(node, opt);
1216
- return null;
1338
+ return replace(opt);
1217
1339
  }
1218
1340
  switch (node.type) {
1219
1341
  case "ConditionalExpression":
@@ -1223,7 +1345,7 @@ async function optimizeMonkeyC(fnMap) {
1223
1345
  const rep = node.test.value ? node.consequent : node.alternate;
1224
1346
  if (!rep)
1225
1347
  return false;
1226
- replace(node, rep);
1348
+ return replace(rep);
1227
1349
  }
1228
1350
  break;
1229
1351
  case "WhileStatement":
@@ -1236,27 +1358,50 @@ async function optimizeMonkeyC(fnMap) {
1236
1358
  return node.body;
1237
1359
  }
1238
1360
  break;
1239
- case "CallExpression": {
1240
- const ret = optimizeCall(state, node, false);
1241
- if (ret) {
1242
- replace(node, ret);
1361
+ case "ReturnStatement":
1362
+ if (node.argument && node.argument.type === "CallExpression") {
1363
+ return replace(optimizeCall(state, node.argument, node));
1243
1364
  }
1244
1365
  break;
1366
+ case "CallExpression": {
1367
+ return replace(optimizeCall(state, node, null));
1245
1368
  }
1369
+ case "AssignmentExpression":
1370
+ if (node.operator === "=" &&
1371
+ node.left.type === "Identifier" &&
1372
+ node.right.type === "Identifier" &&
1373
+ node.left.name === node.right.name) {
1374
+ return { type: "Literal", value: null, raw: "null" };
1375
+ }
1376
+ break;
1246
1377
  case "ExpressionStatement":
1247
1378
  if (node.expression.type === "CallExpression") {
1248
- const ret = optimizeCall(state, node.expression, true);
1249
- if (ret) {
1250
- if (ret.type === "BlockStatement") {
1251
- return ret;
1379
+ return replace(optimizeCall(state, node.expression, node));
1380
+ }
1381
+ else if (node.expression.type === "AssignmentExpression") {
1382
+ if (node.expression.right.type === "CallExpression") {
1383
+ let ok = false;
1384
+ if (node.expression.left.type === "Identifier") {
1385
+ if (hasProperty(topLocals().map, node.expression.left.type)) {
1386
+ ok = true;
1387
+ }
1388
+ }
1389
+ if (!ok && node.expression.operator == "=") {
1390
+ const [, result] = state.lookup(node.expression.left);
1391
+ ok = result != null;
1392
+ }
1393
+ if (ok) {
1394
+ return replace(optimizeCall(state, node.expression.right, node.expression));
1252
1395
  }
1253
- node.expression = ret;
1254
1396
  }
1255
1397
  }
1256
1398
  else {
1257
1399
  const ret = unused(node.expression, true);
1258
1400
  if (ret) {
1259
- return ret;
1401
+ return ret
1402
+ .map(replace)
1403
+ .flat(1)
1404
+ .filter((s) => !!s);
1260
1405
  }
1261
1406
  }
1262
1407
  break;
@@ -1266,6 +1411,11 @@ async function optimizeMonkeyC(fnMap) {
1266
1411
  Object.values(fnMap).forEach((f) => {
1267
1412
  collectNamespaces(f.ast, state);
1268
1413
  });
1414
+ state.calledFunctions = {};
1415
+ state.exposed = {};
1416
+ Object.values(fnMap).forEach((f) => {
1417
+ collectNamespaces(f.ast, state);
1418
+ });
1269
1419
  delete state.pre;
1270
1420
  delete state.post;
1271
1421
  const cleanup = (node) => {
@@ -1294,19 +1444,24 @@ async function optimizeMonkeyC(fnMap) {
1294
1444
  if (!node.body.members.length) {
1295
1445
  if (!node.id)
1296
1446
  return false;
1297
- if (!node.body["enumType"]) {
1447
+ if (!node.body.enumType) {
1298
1448
  throw new Error("Missing enumType on optimized enum");
1299
1449
  }
1300
- replace(node, {
1450
+ return {
1301
1451
  type: "TypedefDeclaration",
1302
1452
  id: node.id,
1303
1453
  ts: {
1304
1454
  type: "UnaryExpression",
1305
- argument: { type: "TypeSpecList", ts: [node.body.enumType] },
1455
+ argument: {
1456
+ type: "TypeSpecList",
1457
+ ts: [
1458
+ node.body.enumType,
1459
+ ],
1460
+ },
1306
1461
  prefix: true,
1307
1462
  operator: " as",
1308
1463
  },
1309
- });
1464
+ };
1310
1465
  }
1311
1466
  break;
1312
1467
  case "VariableDeclaration": {
@@ -1341,9 +1496,12 @@ async function optimizeMonkeyC(fnMap) {
1341
1496
  return ret;
1342
1497
  });
1343
1498
  });
1499
+ return state.diagnostics;
1344
1500
  }
1345
- function optimizeCall(state, node, asStatement) {
1346
- const [name, callees] = state.lookup(node.callee);
1501
+ function optimizeCall(state, node, context) {
1502
+ const [name, results] = state.lookup(node.callee);
1503
+ const callees = results &&
1504
+ results.filter((c) => c.type === "FunctionDeclaration");
1347
1505
  if (!callees || !callees.length) {
1348
1506
  const n = name ||
1349
1507
  ("name" in node.callee && node.callee.name) ||
@@ -1362,7 +1520,8 @@ function optimizeCall(state, node, asStatement) {
1362
1520
  }
1363
1521
  if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
1364
1522
  const callee = callees[0].node;
1365
- if (callee.optimizable &&
1523
+ if (!context &&
1524
+ callee.optimizable &&
1366
1525
  !callee.hasOverride &&
1367
1526
  node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
1368
1527
  const ret = evaluateFunction(callee, node.arguments);
@@ -1370,19 +1529,14 @@ function optimizeCall(state, node, asStatement) {
1370
1529
  return ret;
1371
1530
  }
1372
1531
  }
1373
- const inlineStatus = shouldInline(state, callees[0], node.arguments);
1374
- if (inlineStatus === InlineStatus.AsExpression ||
1375
- (asStatement && inlineStatus === InlineStatus.AsStatement)) {
1376
- const ret = inlineFunction(state, callees[0], node, inlineStatus);
1532
+ if (shouldInline(state, callees[0], node, context)) {
1533
+ const ret = inlineFunction(state, callees[0], node, context);
1377
1534
  if (ret) {
1378
1535
  return ret;
1379
1536
  }
1380
1537
  }
1381
1538
  }
1382
- if (!hasProperty(state.calledFunctions, name)) {
1383
- state.calledFunctions[name] = [];
1384
- }
1385
- callees.forEach((c) => isStateNode(c) && state.calledFunctions[name].push(c.node));
1539
+ callees.forEach((c) => markFunctionCalled(state, c.node));
1386
1540
  return null;
1387
1541
  }
1388
1542
 
@@ -1914,23 +2068,14 @@ function api_traverseAst(node, pre, post) {
1914
2068
  return post && post(node);
1915
2069
  }
1916
2070
  function formatAst(node, monkeyCSource = null) {
1917
- if ("comments" in node && !monkeyCSource) {
1918
- // Prettier inserts comments by using the source location to
1919
- // find the original comment, rather than using the contents
1920
- // of the comment as reported by the comment nodes themselves.
1921
- // If all we've got is the ast, rather than the actual
1922
- // source code, this goes horribly wrong, so just drop all
1923
- // the comments.
1924
- delete node.comments;
1925
- }
1926
2071
  /*
1927
2072
  * The estree printer sometimes looks at the parent node without
1928
2073
  * checking that there *is* a parent node (eg it assumes all
1929
2074
  * BinaryExpressions have a parent node, and crashes if they don't).
1930
- * To avoid issues, wrap nodes in an ExpressionStatement, and
1931
- * then remove the added ";" from the end.
2075
+ * To avoid issues, wrap nodes in an ParenthesizedExpression.
2076
+ * The printer knows that parentless ParenthesizedExpressions
2077
+ * should be ignored.
1932
2078
  */
1933
- let unwrap = false;
1934
2079
  switch (node.type) {
1935
2080
  case "Program":
1936
2081
  case "BlockStatement":
@@ -1938,11 +2083,10 @@ function formatAst(node, monkeyCSource = null) {
1938
2083
  break;
1939
2084
  default: {
1940
2085
  const e = {
1941
- type: "ExpressionStatement",
2086
+ type: "ParenthesizedExpression",
1942
2087
  expression: node,
1943
2088
  };
1944
2089
  node = e;
1945
- unwrap = true;
1946
2090
  }
1947
2091
  }
1948
2092
  // If we *do* have the original source, pass that in ahead of the
@@ -1950,15 +2094,11 @@ function formatAst(node, monkeyCSource = null) {
1950
2094
  // as the ast itself, and the printers will find what they're
1951
2095
  // looking for in the source.
1952
2096
  const source = (monkeyCSource || "") + "\n" + JSON.stringify(node);
1953
- const result = external_prettier_namespaceObject.format(source, {
2097
+ return external_prettier_namespaceObject.format(source, {
1954
2098
  parser: "monkeyc-json",
1955
2099
  plugins: [(prettier_plugin_monkeyc_default())],
1956
2100
  endOfLine: "lf",
1957
2101
  });
1958
- if (unwrap) {
1959
- return result.replace(/;$/, "");
1960
- }
1961
- return result;
1962
2102
  }
1963
2103
  function handleException(state, node, exception) {
1964
2104
  try {