@markw65/monkeyc-optimizer 1.0.17 → 1.0.20
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 +42 -0
- package/build/api.cjs +240 -98
- package/build/optimizer.cjs +281 -111
- 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 +27 -2
- package/build/src/util.d.ts +2 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -193,3 +193,45 @@ More fixes found via open source projects.
|
|
|
193
193
|
|
|
194
194
|
- Bug Fixes
|
|
195
195
|
- Fix a bug affecting lookup of types, which could cause definitions, references and links to the api docs to be missed in the vscode extension
|
|
196
|
+
|
|
197
|
+
### 1.0.18
|
|
198
|
+
|
|
199
|
+
- Bug Fixes
|
|
200
|
+
- The new inliner was too agressive at constant propagating literal parameters to their point of use.
|
|
201
|
+
|
|
202
|
+
### 1.0.19
|
|
203
|
+
|
|
204
|
+
- Upgrade to @markw65/prettier-plugin-monkeyc@1.0.22
|
|
205
|
+
|
|
206
|
+
- fixes some minor typing issues for mctree
|
|
207
|
+
- special handling for certain parenthesized expressions.
|
|
208
|
+
|
|
209
|
+
- Optimizer
|
|
210
|
+
|
|
211
|
+
- Handle more unused expressions, add tests, and prettify the OptimizerTests project
|
|
212
|
+
- Allow statement-style inlining in assignent and return contexts
|
|
213
|
+
- Add diagnostics for failure to inline
|
|
214
|
+
|
|
215
|
+
- Tests
|
|
216
|
+
|
|
217
|
+
- More tweaks to pragma-checker
|
|
218
|
+
- Add launch and task configs for building/running tests
|
|
219
|
+
|
|
220
|
+
- Code cleanup
|
|
221
|
+
- Properly type the results of JSON.parse
|
|
222
|
+
- Switch over to using ParenthesizedExpression for formatAst (depends on @markw65/prettier-plugin-monkeyc@1.0.22)
|
|
223
|
+
|
|
224
|
+
### 1.0.20
|
|
225
|
+
|
|
226
|
+
- Bug fixes
|
|
227
|
+
|
|
228
|
+
- Fix a bug marking unknown callees
|
|
229
|
+
- Fix a bug in test.js that didn't notice when tests failed, and fix a failing test
|
|
230
|
+
|
|
231
|
+
- Optimizer enhancements
|
|
232
|
+
|
|
233
|
+
- Re-run the main optimization step, to properly account for functions that are unused after optimization
|
|
234
|
+
- Call the optimizer on 'unused' nodes before returning them
|
|
235
|
+
|
|
236
|
+
- Code cleanup
|
|
237
|
+
- Called function cleanup
|
package/build/api.cjs
CHANGED
|
@@ -181,7 +181,7 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
181
181
|
})) {
|
|
182
182
|
return false;
|
|
183
183
|
}
|
|
184
|
-
if (allSafe)
|
|
184
|
+
if (allSafe && requireAll)
|
|
185
185
|
return true;
|
|
186
186
|
let callSeen = false;
|
|
187
187
|
let ok = true;
|
|
@@ -204,10 +204,16 @@ function getArgSafety(state, func, args, requireAll) {
|
|
|
204
204
|
.map(([key]) => key);
|
|
205
205
|
}, (node) => {
|
|
206
206
|
switch (node.type) {
|
|
207
|
+
case "AssignmentExpression":
|
|
208
|
+
case "UpdateExpression": {
|
|
209
|
+
const v = node.type == "UpdateExpression" ? node.argument : node.left;
|
|
210
|
+
if (v.type === "Identifier" && hasProperty(params, v.name)) {
|
|
211
|
+
safeArgs[params[v.name]] = null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// fall through
|
|
207
215
|
case "CallExpression":
|
|
208
216
|
case "NewExpression":
|
|
209
|
-
case "AssignmentExpression":
|
|
210
|
-
case "UpdateExpression":
|
|
211
217
|
callSeen = true;
|
|
212
218
|
break;
|
|
213
219
|
case "Identifier":
|
|
@@ -250,15 +256,30 @@ function inliningLooksUseful(func, node) {
|
|
|
250
256
|
}
|
|
251
257
|
return false;
|
|
252
258
|
}
|
|
253
|
-
var
|
|
259
|
+
var InlineStatus;
|
|
254
260
|
(function (InlineStatus) {
|
|
255
261
|
InlineStatus[InlineStatus["Never"] = 0] = "Never";
|
|
256
262
|
InlineStatus[InlineStatus["AsExpression"] = 1] = "AsExpression";
|
|
257
263
|
InlineStatus[InlineStatus["AsStatement"] = 2] = "AsStatement";
|
|
258
|
-
})(
|
|
259
|
-
function
|
|
264
|
+
})(InlineStatus || (InlineStatus = {}));
|
|
265
|
+
function inlineRequested(state, func) {
|
|
266
|
+
const excludeAnnotations = (func.node.loc?.source &&
|
|
267
|
+
state.fnMap[func.node.loc?.source]?.excludeAnnotations) ||
|
|
268
|
+
{};
|
|
269
|
+
if (func.node.attrs &&
|
|
270
|
+
func.node.attrs.attrs &&
|
|
271
|
+
func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
|
|
272
|
+
(attr.argument.name === "inline" ||
|
|
273
|
+
(attr.argument.name.startsWith("inline_") &&
|
|
274
|
+
hasProperty(excludeAnnotations, attr.argument.name.substring(7)))))) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
function inliner_shouldInline(state, func, call, context) {
|
|
260
280
|
let autoInline = false;
|
|
261
281
|
let inlineAsExpression = false;
|
|
282
|
+
const args = call.arguments;
|
|
262
283
|
if (func.node.body &&
|
|
263
284
|
func.node.body.body.length === 1 &&
|
|
264
285
|
func.node.body.body[0].type === "ReturnStatement" &&
|
|
@@ -268,39 +289,30 @@ function inliner_shouldInline(state, func, args) {
|
|
|
268
289
|
autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
|
|
269
290
|
}
|
|
270
291
|
if (autoInline === 1) {
|
|
271
|
-
return
|
|
292
|
+
return true;
|
|
272
293
|
}
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return
|
|
284
|
-
? inliner_InlineStatus.AsExpression
|
|
285
|
-
: inliner_InlineStatus.AsStatement;
|
|
294
|
+
const requested = inlineRequested(state, func);
|
|
295
|
+
if (autoInline || requested) {
|
|
296
|
+
if (inlineAsExpression) {
|
|
297
|
+
if (canInline(state, func, args)) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!context && requested) {
|
|
302
|
+
inlineDiagnostic(state, func, call, "This function can only be inlined in statement, assignment, or return contexts");
|
|
303
|
+
}
|
|
304
|
+
return context != null;
|
|
286
305
|
}
|
|
287
|
-
return
|
|
306
|
+
return false;
|
|
288
307
|
}
|
|
289
308
|
function processInlineBody(state, func, call, root, insertedVariableDecls, params) {
|
|
290
|
-
|
|
291
|
-
const safeArgs = getArgSafety(state, func, call.arguments, false);
|
|
292
|
-
params = Object.fromEntries(func.node.params.map((param, i) => {
|
|
293
|
-
const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
|
|
294
|
-
? i
|
|
295
|
-
: -1;
|
|
296
|
-
const name = variableDeclarationName(param);
|
|
297
|
-
return [name, argnum];
|
|
298
|
-
}));
|
|
299
|
-
}
|
|
309
|
+
let failed = false;
|
|
300
310
|
const pre = state.pre;
|
|
301
311
|
const post = state.post;
|
|
302
312
|
try {
|
|
303
313
|
state.pre = (node) => {
|
|
314
|
+
if (failed)
|
|
315
|
+
return [];
|
|
304
316
|
node.start = call.start;
|
|
305
317
|
node.end = call.end;
|
|
306
318
|
node.loc = call.loc;
|
|
@@ -312,6 +324,11 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
312
324
|
const { map } = locals;
|
|
313
325
|
if (!map)
|
|
314
326
|
throw new Error("No local variable map!");
|
|
327
|
+
// We still need to keep track of every local name that was
|
|
328
|
+
// already in use, but we don't want to use any of its renames.
|
|
329
|
+
// We also want to know whether a local is from the function being
|
|
330
|
+
// inlined, or the calling function, so set every element to false.
|
|
331
|
+
Object.keys(map).forEach((key) => (map[key] = false));
|
|
315
332
|
const declarations = func.node.params
|
|
316
333
|
.map((param, i) => {
|
|
317
334
|
const paramName = variableDeclarationName(param);
|
|
@@ -336,6 +353,8 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
336
353
|
return result;
|
|
337
354
|
};
|
|
338
355
|
state.post = (node) => {
|
|
356
|
+
if (failed)
|
|
357
|
+
return null;
|
|
339
358
|
let replacement = null;
|
|
340
359
|
switch (node.type) {
|
|
341
360
|
case "Identifier": {
|
|
@@ -350,22 +369,28 @@ function processInlineBody(state, func, call, root, insertedVariableDecls, param
|
|
|
350
369
|
}
|
|
351
370
|
replacement = fixNodeScope(state, node, func.stack);
|
|
352
371
|
if (!replacement) {
|
|
353
|
-
|
|
372
|
+
failed = true;
|
|
373
|
+
inlineDiagnostic(state, func, call, `Failed to resolve '${node.name}'`);
|
|
374
|
+
return null;
|
|
354
375
|
}
|
|
355
376
|
break;
|
|
356
377
|
}
|
|
357
378
|
}
|
|
358
379
|
return post(replacement || node, state) || replacement;
|
|
359
380
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
381
|
+
let ret = state.traverse(root);
|
|
382
|
+
if (failed) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
if (ret === null) {
|
|
386
|
+
ret = root;
|
|
387
|
+
}
|
|
388
|
+
if (!ret) {
|
|
389
|
+
inlineDiagnostic(state, func, call, `Internal error`);
|
|
390
|
+
return null;
|
|
367
391
|
}
|
|
368
|
-
|
|
392
|
+
inlineDiagnostic(state, func, call, null);
|
|
393
|
+
return ret;
|
|
369
394
|
}
|
|
370
395
|
finally {
|
|
371
396
|
state.pre = pre;
|
|
@@ -388,7 +413,16 @@ function inliner_unused(expression, top) {
|
|
|
388
413
|
case "UnaryExpression":
|
|
389
414
|
return inliner_unused(expression.argument);
|
|
390
415
|
case "MemberExpression":
|
|
391
|
-
|
|
416
|
+
if (expression.computed) {
|
|
417
|
+
return inliner_unused(expression.object).concat(inliner_unused(expression.property));
|
|
418
|
+
}
|
|
419
|
+
return inliner_unused(expression.object);
|
|
420
|
+
case "ArrayExpression":
|
|
421
|
+
return expression.elements.map((e) => inliner_unused(e)).flat(1);
|
|
422
|
+
case "ObjectExpression":
|
|
423
|
+
return expression.properties
|
|
424
|
+
.map((p) => inliner_unused(p.key).concat(inliner_unused(p.value)))
|
|
425
|
+
.flat(1);
|
|
392
426
|
}
|
|
393
427
|
return top
|
|
394
428
|
? null
|
|
@@ -396,30 +430,98 @@ function inliner_unused(expression, top) {
|
|
|
396
430
|
{
|
|
397
431
|
type: "ExpressionStatement",
|
|
398
432
|
expression,
|
|
433
|
+
start: expression.start,
|
|
434
|
+
end: expression.end,
|
|
435
|
+
loc: expression.loc,
|
|
399
436
|
},
|
|
400
437
|
];
|
|
401
438
|
}
|
|
402
|
-
function
|
|
439
|
+
function diagnostic(state, loc, message) {
|
|
440
|
+
if (!loc || !loc.source)
|
|
441
|
+
return;
|
|
442
|
+
const source = loc.source;
|
|
443
|
+
if (!state.diagnostics)
|
|
444
|
+
state.diagnostics = {};
|
|
445
|
+
if (!hasProperty(state.diagnostics, source)) {
|
|
446
|
+
if (!message)
|
|
447
|
+
return;
|
|
448
|
+
state.diagnostics[source] = [];
|
|
449
|
+
}
|
|
450
|
+
const diags = state.diagnostics[source];
|
|
451
|
+
let index = diags.findIndex((item) => item.loc === loc);
|
|
452
|
+
if (message) {
|
|
453
|
+
if (index < 0)
|
|
454
|
+
index = diags.length;
|
|
455
|
+
diags[index] = { type: "INFO", loc, message };
|
|
456
|
+
}
|
|
457
|
+
else if (index >= 0) {
|
|
458
|
+
diags.splice(index, 1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function inlineDiagnostic(state, func, call, message) {
|
|
462
|
+
if (inlineRequested(state, func)) {
|
|
463
|
+
diagnostic(state, call.loc, message && `While inlining ${func.node.id.name}: ${message}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function inlineWithArgs(state, func, call, context) {
|
|
403
467
|
if (!func.node || !func.node.body) {
|
|
404
468
|
return null;
|
|
405
469
|
}
|
|
406
470
|
let retStmtCount = 0;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
471
|
+
if (context.type === "ReturnStatement") {
|
|
472
|
+
const last = func.node.body.body.slice(-1)[0];
|
|
473
|
+
if (!last || last.type !== "ReturnStatement") {
|
|
474
|
+
inlineDiagnostic(state, func, call, "Function didn't end with a return statement");
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
traverseAst(func.node.body, (node) => {
|
|
480
|
+
node.type === "ReturnStatement" && retStmtCount++;
|
|
481
|
+
});
|
|
482
|
+
if (retStmtCount > 1) {
|
|
483
|
+
inlineDiagnostic(state, func, call, "Function had more than one return statement");
|
|
484
|
+
}
|
|
485
|
+
else if (context.type === "AssignmentExpression" && retStmtCount !== 1) {
|
|
486
|
+
inlineDiagnostic(state, func, call, "Function did not have a return statement");
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
if (retStmtCount === 1) {
|
|
490
|
+
const last = func.node.body.body.slice(-1)[0];
|
|
491
|
+
if (!last ||
|
|
492
|
+
last.type !== "ReturnStatement" ||
|
|
493
|
+
(context.type === "AssignmentExpression" && !last.argument)) {
|
|
494
|
+
inlineDiagnostic(state, func, call, "There was a return statement, but not at the end of the function");
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
414
498
|
}
|
|
415
499
|
const body = JSON.parse(JSON.stringify(func.node.body));
|
|
416
|
-
|
|
417
|
-
|
|
500
|
+
const safeArgs = getArgSafety(state, func, call.arguments, false);
|
|
501
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => {
|
|
502
|
+
const argnum = safeArgs === true || (safeArgs !== false && safeArgs[i] !== null)
|
|
503
|
+
? i
|
|
504
|
+
: -1;
|
|
505
|
+
const name = variableDeclarationName(param);
|
|
506
|
+
return [name, argnum];
|
|
507
|
+
}));
|
|
508
|
+
if (!processInlineBody(state, func, call, body, func.node.params.length ? false : true, params)) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
diagnostic(state, call.loc, null);
|
|
512
|
+
if (context.type !== "ReturnStatement" && retStmtCount) {
|
|
418
513
|
const last = body.body[body.body.length - 1];
|
|
419
514
|
if (last.type != "ReturnStatement") {
|
|
420
515
|
throw new Error("ReturnStatement got lost!");
|
|
421
516
|
}
|
|
422
|
-
if (
|
|
517
|
+
if (context.type === "AssignmentExpression") {
|
|
518
|
+
context.right = last.argument;
|
|
519
|
+
body.body[body.body.length - 1] = {
|
|
520
|
+
type: "ExpressionStatement",
|
|
521
|
+
expression: context,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
else if (last.argument) {
|
|
423
525
|
const side_exprs = inliner_unused(last.argument);
|
|
424
526
|
body.body.splice(body.body.length - 1, 1, ...side_exprs);
|
|
425
527
|
}
|
|
@@ -429,13 +531,13 @@ function inlineWithArgs(state, func, call) {
|
|
|
429
531
|
}
|
|
430
532
|
return body;
|
|
431
533
|
}
|
|
432
|
-
function inliner_inlineFunction(state, func, call,
|
|
433
|
-
if (
|
|
434
|
-
return inlineWithArgs(state, func, call);
|
|
534
|
+
function inliner_inlineFunction(state, func, call, context) {
|
|
535
|
+
if (context) {
|
|
536
|
+
return inlineWithArgs(state, func, call, context);
|
|
435
537
|
}
|
|
436
538
|
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
437
539
|
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
438
|
-
return processInlineBody(state, func, call, retArg, true, params)
|
|
540
|
+
return processInlineBody(state, func, call, retArg, true, params);
|
|
439
541
|
}
|
|
440
542
|
function applyTypeIfNeeded(node) {
|
|
441
543
|
if ("enumType" in node && node.enumType) {
|
|
@@ -450,13 +552,14 @@ function applyTypeIfNeeded(node) {
|
|
|
450
552
|
}
|
|
451
553
|
function fixNodeScope(state, lookupNode, nodeStack) {
|
|
452
554
|
if (lookupNode.type === "Identifier") {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
555
|
+
const locals = state.localsStack[state.localsStack.length - 1];
|
|
556
|
+
const { map } = locals;
|
|
557
|
+
if (!map)
|
|
558
|
+
throw new Error("No local variable map!");
|
|
559
|
+
if (hasProperty(map, lookupNode.name) && map[lookupNode.name] !== false) {
|
|
560
|
+
// map[name] !== false means its an entry that was created during inlining
|
|
561
|
+
// so its definitely one of our locals.
|
|
562
|
+
return lookupNode;
|
|
460
563
|
}
|
|
461
564
|
}
|
|
462
565
|
const [, original] = state.lookup(lookupNode, null, nodeStack);
|
|
@@ -932,13 +1035,15 @@ function optimizeNode(node) {
|
|
|
932
1035
|
return null;
|
|
933
1036
|
}
|
|
934
1037
|
function evaluateFunction(func, args) {
|
|
935
|
-
if (args && args.length != func.params.length) {
|
|
1038
|
+
if (!func.body || (args && args.length != func.params.length)) {
|
|
936
1039
|
return false;
|
|
937
1040
|
}
|
|
938
1041
|
const paramValues = args &&
|
|
939
1042
|
Object.fromEntries(func.params.map((p, i) => [variableDeclarationName(p), args[i]]));
|
|
940
1043
|
let ret = null;
|
|
941
|
-
const body = args
|
|
1044
|
+
const body = args
|
|
1045
|
+
? JSON.parse(JSON.stringify(func.body))
|
|
1046
|
+
: func.body;
|
|
942
1047
|
try {
|
|
943
1048
|
traverseAst(body, (node) => {
|
|
944
1049
|
switch (node.type) {
|
|
@@ -981,6 +1086,13 @@ function evaluateFunction(func, args) {
|
|
|
981
1086
|
return false;
|
|
982
1087
|
}
|
|
983
1088
|
}
|
|
1089
|
+
function markFunctionCalled(state, func) {
|
|
1090
|
+
if (!hasProperty(state.calledFunctions, func.id.name)) {
|
|
1091
|
+
state.calledFunctions[func.id.name] = [func];
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
pushUnique(state.calledFunctions[func.id.name], func);
|
|
1095
|
+
}
|
|
984
1096
|
async function optimizeMonkeyC(fnMap) {
|
|
985
1097
|
const state = {
|
|
986
1098
|
...(await analyze(fnMap)),
|
|
@@ -1139,7 +1251,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1139
1251
|
if (map) {
|
|
1140
1252
|
if (hasProperty(map, node.name)) {
|
|
1141
1253
|
const name = map[node.name];
|
|
1142
|
-
if (name
|
|
1254
|
+
if (typeof name === "string") {
|
|
1143
1255
|
node.name = name;
|
|
1144
1256
|
}
|
|
1145
1257
|
}
|
|
@@ -1190,10 +1302,7 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1190
1302
|
used = checkInherited(parent, node.id.name);
|
|
1191
1303
|
}
|
|
1192
1304
|
if (used) {
|
|
1193
|
-
|
|
1194
|
-
state.calledFunctions[node.id.name] = [];
|
|
1195
|
-
}
|
|
1196
|
-
state.calledFunctions[node.id.name].push(node);
|
|
1305
|
+
markFunctionCalled(state, node);
|
|
1197
1306
|
}
|
|
1198
1307
|
}
|
|
1199
1308
|
}
|
|
@@ -1230,27 +1339,61 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1230
1339
|
return node.body;
|
|
1231
1340
|
}
|
|
1232
1341
|
break;
|
|
1342
|
+
case "ReturnStatement":
|
|
1343
|
+
if (node.argument && node.argument.type === "CallExpression") {
|
|
1344
|
+
return optimizeCall(state, node.argument, node);
|
|
1345
|
+
}
|
|
1346
|
+
break;
|
|
1233
1347
|
case "CallExpression": {
|
|
1234
|
-
const ret = optimizeCall(state, node,
|
|
1348
|
+
const ret = optimizeCall(state, node, null);
|
|
1235
1349
|
if (ret) {
|
|
1236
1350
|
replace(node, ret);
|
|
1237
1351
|
}
|
|
1238
1352
|
break;
|
|
1239
1353
|
}
|
|
1354
|
+
case "AssignmentExpression":
|
|
1355
|
+
if (node.operator === "=" &&
|
|
1356
|
+
node.left.type === "Identifier" &&
|
|
1357
|
+
node.right.type === "Identifier" &&
|
|
1358
|
+
node.left.name === node.right.name) {
|
|
1359
|
+
return { type: "Literal", value: null, raw: "null" };
|
|
1360
|
+
}
|
|
1361
|
+
break;
|
|
1240
1362
|
case "ExpressionStatement":
|
|
1241
1363
|
if (node.expression.type === "CallExpression") {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1364
|
+
return optimizeCall(state, node.expression, node);
|
|
1365
|
+
}
|
|
1366
|
+
else if (node.expression.type === "AssignmentExpression") {
|
|
1367
|
+
if (node.expression.right.type === "CallExpression") {
|
|
1368
|
+
let ok = false;
|
|
1369
|
+
if (node.expression.left.type === "Identifier") {
|
|
1370
|
+
if (hasProperty(topLocals().map, node.expression.left.type)) {
|
|
1371
|
+
ok = true;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (!ok && node.expression.operator == "=") {
|
|
1375
|
+
const [, result] = state.lookup(node.expression.left);
|
|
1376
|
+
ok = result != null;
|
|
1377
|
+
}
|
|
1378
|
+
if (ok) {
|
|
1379
|
+
const ret = optimizeCall(state, node.expression.right, node.expression);
|
|
1380
|
+
if (ret && ret.type === "BlockStatement") {
|
|
1381
|
+
const r2 = state.traverse(ret);
|
|
1382
|
+
return r2 === false || r2 ? r2 : ret;
|
|
1383
|
+
}
|
|
1246
1384
|
}
|
|
1247
|
-
node.expression = ret;
|
|
1248
1385
|
}
|
|
1249
1386
|
}
|
|
1250
1387
|
else {
|
|
1251
1388
|
const ret = unused(node.expression, true);
|
|
1252
1389
|
if (ret) {
|
|
1253
|
-
return ret
|
|
1390
|
+
return ret
|
|
1391
|
+
.map((s) => {
|
|
1392
|
+
const r2 = state.traverse(s);
|
|
1393
|
+
return r2 === false || r2 ? r2 : s;
|
|
1394
|
+
})
|
|
1395
|
+
.flat(1)
|
|
1396
|
+
.filter((s) => s !== false);
|
|
1254
1397
|
}
|
|
1255
1398
|
}
|
|
1256
1399
|
break;
|
|
@@ -1260,6 +1403,11 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1260
1403
|
Object.values(fnMap).forEach((f) => {
|
|
1261
1404
|
collectNamespaces(f.ast, state);
|
|
1262
1405
|
});
|
|
1406
|
+
state.calledFunctions = {};
|
|
1407
|
+
state.exposed = {};
|
|
1408
|
+
Object.values(fnMap).forEach((f) => {
|
|
1409
|
+
collectNamespaces(f.ast, state);
|
|
1410
|
+
});
|
|
1263
1411
|
delete state.pre;
|
|
1264
1412
|
delete state.post;
|
|
1265
1413
|
const cleanup = (node) => {
|
|
@@ -1335,9 +1483,12 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1335
1483
|
return ret;
|
|
1336
1484
|
});
|
|
1337
1485
|
});
|
|
1486
|
+
return state.diagnostics;
|
|
1338
1487
|
}
|
|
1339
|
-
function optimizeCall(state, node,
|
|
1340
|
-
const [name,
|
|
1488
|
+
function optimizeCall(state, node, context) {
|
|
1489
|
+
const [name, results] = state.lookup(node.callee);
|
|
1490
|
+
const callees = results &&
|
|
1491
|
+
results.filter((c) => c.type === "FunctionDeclaration");
|
|
1341
1492
|
if (!callees || !callees.length) {
|
|
1342
1493
|
const n = name ||
|
|
1343
1494
|
("name" in node.callee && node.callee.name) ||
|
|
@@ -1356,7 +1507,8 @@ function optimizeCall(state, node, asStatement) {
|
|
|
1356
1507
|
}
|
|
1357
1508
|
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
1358
1509
|
const callee = callees[0].node;
|
|
1359
|
-
if (
|
|
1510
|
+
if (!context &&
|
|
1511
|
+
callee.optimizable &&
|
|
1360
1512
|
!callee.hasOverride &&
|
|
1361
1513
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
1362
1514
|
const ret = evaluateFunction(callee, node.arguments);
|
|
@@ -1364,19 +1516,14 @@ function optimizeCall(state, node, asStatement) {
|
|
|
1364
1516
|
return ret;
|
|
1365
1517
|
}
|
|
1366
1518
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
(asStatement && inlineStatus === InlineStatus.AsStatement)) {
|
|
1370
|
-
const ret = inlineFunction(state, callees[0], node, inlineStatus);
|
|
1519
|
+
if (shouldInline(state, callees[0], node, context)) {
|
|
1520
|
+
const ret = inlineFunction(state, callees[0], node, context);
|
|
1371
1521
|
if (ret) {
|
|
1372
1522
|
return ret;
|
|
1373
1523
|
}
|
|
1374
1524
|
}
|
|
1375
1525
|
}
|
|
1376
|
-
|
|
1377
|
-
state.calledFunctions[name] = [];
|
|
1378
|
-
}
|
|
1379
|
-
callees.forEach((c) => isStateNode(c) && state.calledFunctions[name].push(c.node));
|
|
1526
|
+
callees.forEach((c) => markFunctionCalled(state, c.node));
|
|
1380
1527
|
return null;
|
|
1381
1528
|
}
|
|
1382
1529
|
|
|
@@ -1921,10 +2068,10 @@ function formatAst(node, monkeyCSource = null) {
|
|
|
1921
2068
|
* The estree printer sometimes looks at the parent node without
|
|
1922
2069
|
* checking that there *is* a parent node (eg it assumes all
|
|
1923
2070
|
* BinaryExpressions have a parent node, and crashes if they don't).
|
|
1924
|
-
* To avoid issues, wrap nodes in an
|
|
1925
|
-
*
|
|
2071
|
+
* To avoid issues, wrap nodes in an ParenthesizedExpression.
|
|
2072
|
+
* The printer knows that parentless ParenthesizedExpressions
|
|
2073
|
+
* should be ignored.
|
|
1926
2074
|
*/
|
|
1927
|
-
let unwrap = false;
|
|
1928
2075
|
switch (node.type) {
|
|
1929
2076
|
case "Program":
|
|
1930
2077
|
case "BlockStatement":
|
|
@@ -1932,11 +2079,10 @@ function formatAst(node, monkeyCSource = null) {
|
|
|
1932
2079
|
break;
|
|
1933
2080
|
default: {
|
|
1934
2081
|
const e = {
|
|
1935
|
-
type: "
|
|
2082
|
+
type: "ParenthesizedExpression",
|
|
1936
2083
|
expression: node,
|
|
1937
2084
|
};
|
|
1938
2085
|
node = e;
|
|
1939
|
-
unwrap = true;
|
|
1940
2086
|
}
|
|
1941
2087
|
}
|
|
1942
2088
|
// If we *do* have the original source, pass that in ahead of the
|
|
@@ -1944,15 +2090,11 @@ function formatAst(node, monkeyCSource = null) {
|
|
|
1944
2090
|
// as the ast itself, and the printers will find what they're
|
|
1945
2091
|
// looking for in the source.
|
|
1946
2092
|
const source = (monkeyCSource || "") + "\n" + JSON.stringify(node);
|
|
1947
|
-
|
|
2093
|
+
return external_prettier_namespaceObject.format(source, {
|
|
1948
2094
|
parser: "monkeyc-json",
|
|
1949
2095
|
plugins: [(prettier_plugin_monkeyc_default())],
|
|
1950
2096
|
endOfLine: "lf",
|
|
1951
2097
|
});
|
|
1952
|
-
if (unwrap) {
|
|
1953
|
-
return result.replace(/;$/, "");
|
|
1954
|
-
}
|
|
1955
|
-
return result;
|
|
1956
2098
|
}
|
|
1957
2099
|
function handleException(state, node, exception) {
|
|
1958
2100
|
try {
|