@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/README.md +46 -0
- package/build/api.cjs +275 -135
- package/build/optimizer.cjs +316 -139
- package/build/src/inliner.d.ts +3 -2
- package/build/src/launch.d.ts +2 -1
- package/build/src/mc-rewrite.d.ts +8 -1
- package/build/src/optimizer.d.ts +28 -2
- package/build/src/util.d.ts +2 -3
- package/package.json +2 -2
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
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
|
-
})(
|
|
265
|
-
function
|
|
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
|
|
303
|
+
return true;
|
|
278
304
|
}
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
{
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return
|
|
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
|
|
317
|
+
return false;
|
|
294
318
|
}
|
|
295
319
|
function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
|
|
296
|
-
|
|
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
|
-
|
|
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
|
-
|
|
391
|
+
const ret = post(replacement || node, state);
|
|
392
|
+
return ret === false || ret ? ret : replacement;
|
|
365
393
|
};
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
423
|
-
|
|
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 (
|
|
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,
|
|
439
|
-
if (
|
|
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)
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
"
|
|
686
|
-
node.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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
1039
|
-
func.attrs.
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 "
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
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
|
|
1447
|
+
if (!node.body.enumType) {
|
|
1298
1448
|
throw new Error("Missing enumType on optimized enum");
|
|
1299
1449
|
}
|
|
1300
|
-
|
|
1450
|
+
return {
|
|
1301
1451
|
type: "TypedefDeclaration",
|
|
1302
1452
|
id: node.id,
|
|
1303
1453
|
ts: {
|
|
1304
1454
|
type: "UnaryExpression",
|
|
1305
|
-
argument: {
|
|
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,
|
|
1346
|
-
const [name,
|
|
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 (
|
|
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
|
-
|
|
1374
|
-
|
|
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
|
-
|
|
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
|
|
1931
|
-
*
|
|
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: "
|
|
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
|
-
|
|
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 {
|