@markw65/monkeyc-optimizer 1.0.15 → 1.0.16
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 +17 -0
- package/build/api.cjs +346 -37
- package/build/optimizer.cjs +317 -26
- package/build/src/inliner.d.ts +4 -0
- package/build/src/optimizer.d.ts +18 -3
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -161,3 +161,20 @@ More fixes found via open source projects.
|
|
|
161
161
|
- Bug fixes
|
|
162
162
|
- Inject the superclass name into the classes namespace
|
|
163
163
|
- Separate type vs value lookup, and use the correct one based on context.
|
|
164
|
+
|
|
165
|
+
### 1.0.16
|
|
166
|
+
|
|
167
|
+
- Bug fixes
|
|
168
|
+
|
|
169
|
+
- Fix off-by-one in removeNodeComments
|
|
170
|
+
- Fix lookup to consistently lookup types or values.
|
|
171
|
+
- Fix lookup of superclass names
|
|
172
|
+
|
|
173
|
+
- New Features
|
|
174
|
+
|
|
175
|
+
- Add a simple inliner
|
|
176
|
+
- Add support for conditional inlining based on excludeAnnotations
|
|
177
|
+
|
|
178
|
+
- Testing
|
|
179
|
+
- Add support for @match pragmas to check the optimization results
|
|
180
|
+
- Add a test project, with some inlining tests
|
package/build/api.cjs
CHANGED
|
@@ -233,6 +233,276 @@ const promises_namespaceObject = require("fs/promises");
|
|
|
233
233
|
var standalone = __webpack_require__(3945);
|
|
234
234
|
;// CONCATENATED MODULE: external "./api.cjs"
|
|
235
235
|
const external_api_cjs_namespaceObject = require("./api.cjs");
|
|
236
|
+
;// CONCATENATED MODULE: ./src/inliner.ts
|
|
237
|
+
|
|
238
|
+
function canInline(state, func, args) {
|
|
239
|
+
// determine whether decl might be changed by a function call
|
|
240
|
+
// during the evaluation of FunctionStateNode.
|
|
241
|
+
const getSafety = (decl) => {
|
|
242
|
+
// enums are constant, they cant change
|
|
243
|
+
if (decl.type === "EnumStringMember")
|
|
244
|
+
return true;
|
|
245
|
+
if (decl.type === "VariableDeclarator") {
|
|
246
|
+
// constants also can't change
|
|
247
|
+
if (decl.node.kind === "const")
|
|
248
|
+
return true;
|
|
249
|
+
// if decl is a local, it also can't be changed
|
|
250
|
+
// by a call to another function.
|
|
251
|
+
for (let i = 0;; i++) {
|
|
252
|
+
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
253
|
+
return false;
|
|
254
|
+
if (state.stack[i].type === "FunctionDeclaration")
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
};
|
|
260
|
+
const safeArgs = [];
|
|
261
|
+
let allSafe = true;
|
|
262
|
+
if (!args.every((arg) => {
|
|
263
|
+
switch (arg.type) {
|
|
264
|
+
case "Literal":
|
|
265
|
+
safeArgs.push(true);
|
|
266
|
+
return true;
|
|
267
|
+
case "Identifier":
|
|
268
|
+
case "MemberExpression": {
|
|
269
|
+
const [, results] = state.lookup(arg);
|
|
270
|
+
if (!results || results.length !== 1)
|
|
271
|
+
return false;
|
|
272
|
+
const safety = getSafety(results[0]);
|
|
273
|
+
if (safety === null)
|
|
274
|
+
return false;
|
|
275
|
+
if (!safety)
|
|
276
|
+
allSafe = false;
|
|
277
|
+
safeArgs.push(safety);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
})) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
if (allSafe)
|
|
286
|
+
return true;
|
|
287
|
+
let callSeen = false;
|
|
288
|
+
let ok = true;
|
|
289
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
290
|
+
const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
|
|
291
|
+
// look for uses of "unsafe" args that occur after a call.
|
|
292
|
+
// use post to do the checking, because arguments are evaluated
|
|
293
|
+
// prior to the call, so eg "return f(x.y);" is fine, but
|
|
294
|
+
// "return f()+x.y" is not.
|
|
295
|
+
//
|
|
296
|
+
// We also have to use a "pre" to ensure that child nodes are
|
|
297
|
+
// visited in source order (otherwise we could visit x.y before f()
|
|
298
|
+
// in the above example)
|
|
299
|
+
traverseAst(func.node.body, (node) => {
|
|
300
|
+
return Object.entries(node)
|
|
301
|
+
.filter((kv) => Array.isArray(kv[1])
|
|
302
|
+
? kv[1].length !== 0 && hasProperty(kv[1][0], "type")
|
|
303
|
+
: hasProperty(kv[1], "type"))
|
|
304
|
+
.sort(([, a], [, b]) => getLoc(a) - getLoc(b))
|
|
305
|
+
.map(([key]) => key);
|
|
306
|
+
}, (node) => {
|
|
307
|
+
switch (node.type) {
|
|
308
|
+
case "CallExpression":
|
|
309
|
+
case "NewExpression":
|
|
310
|
+
callSeen = true;
|
|
311
|
+
break;
|
|
312
|
+
case "Identifier":
|
|
313
|
+
if (callSeen &&
|
|
314
|
+
hasProperty(params, node.name) &&
|
|
315
|
+
!safeArgs[params[node.name]]) {
|
|
316
|
+
ok = false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
return ok;
|
|
321
|
+
}
|
|
322
|
+
function inliningLooksUseful(func, node) {
|
|
323
|
+
while (true) {
|
|
324
|
+
if (node.type === "BinaryExpression" && node.operator === "as") {
|
|
325
|
+
node = node.left;
|
|
326
|
+
}
|
|
327
|
+
else if (node.type === "UnaryExpression" && node.operator === " as") {
|
|
328
|
+
node = node.argument;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (node.type === "Literal")
|
|
335
|
+
return true;
|
|
336
|
+
if (node.type === "Identifier") {
|
|
337
|
+
if (func.params.length === 1 &&
|
|
338
|
+
variableDeclarationName(func.params[0]) === node.name) {
|
|
339
|
+
return 1;
|
|
340
|
+
}
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
function inliner_shouldInline(state, func, args) {
|
|
346
|
+
if (!func.node.body ||
|
|
347
|
+
func.node.body.body.length !== 1 ||
|
|
348
|
+
func.node.body.body[0].type !== "ReturnStatement" ||
|
|
349
|
+
!func.node.body.body[0].argument ||
|
|
350
|
+
func.node.params.length !== args.length) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
|
|
354
|
+
const excludeAnnotations = (func.node.loc?.source &&
|
|
355
|
+
state.fnMap[func.node.loc?.source].excludeAnnotations) ||
|
|
356
|
+
{};
|
|
357
|
+
return ((autoInline ||
|
|
358
|
+
(func.node.attrs &&
|
|
359
|
+
func.node.attrs.attrs &&
|
|
360
|
+
func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
|
|
361
|
+
(attr.argument.name === "inline" ||
|
|
362
|
+
(attr.argument.name.startsWith("inline_") &&
|
|
363
|
+
hasProperty(excludeAnnotations, attr.argument.name.substring(7))))))) &&
|
|
364
|
+
(autoInline === 1 || canInline(state, func, args)));
|
|
365
|
+
}
|
|
366
|
+
function inliner_inlineFunction(state, func, call) {
|
|
367
|
+
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
368
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
|
|
369
|
+
try {
|
|
370
|
+
const result = traverseAst(retArg, (node) => {
|
|
371
|
+
switch (node.type) {
|
|
372
|
+
case "MemberExpression":
|
|
373
|
+
if (!node.computed) {
|
|
374
|
+
return ["object"];
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
case "BinaryExpression":
|
|
378
|
+
if (node.operator === "as") {
|
|
379
|
+
return ["left"];
|
|
380
|
+
}
|
|
381
|
+
break;
|
|
382
|
+
case "UnaryExpression":
|
|
383
|
+
if (node.operator === " as") {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}, (node) => {
|
|
389
|
+
switch (node.type) {
|
|
390
|
+
case "Identifier": {
|
|
391
|
+
if (hasProperty(params, node.name)) {
|
|
392
|
+
return call.arguments[params[node.name]];
|
|
393
|
+
}
|
|
394
|
+
const rep = fixNodeScope(state, node, func.stack);
|
|
395
|
+
if (!rep) {
|
|
396
|
+
throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
|
|
397
|
+
}
|
|
398
|
+
return rep;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}) || retArg;
|
|
403
|
+
result.loc = call.loc;
|
|
404
|
+
result.start = call.start;
|
|
405
|
+
result.end = call.end;
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
catch (ex) {
|
|
409
|
+
if (ex instanceof Error) {
|
|
410
|
+
if (ex.message.startsWith("Inliner: ")) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
throw ex;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function applyTypeIfNeeded(node) {
|
|
418
|
+
if ("enumType" in node && node.enumType) {
|
|
419
|
+
node = {
|
|
420
|
+
type: "BinaryExpression",
|
|
421
|
+
operator: "as",
|
|
422
|
+
left: node,
|
|
423
|
+
right: { type: "TypeSpecList", ts: [node.enumType] },
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
return node;
|
|
427
|
+
}
|
|
428
|
+
function fixNodeScope(state, lookupNode, nodeStack) {
|
|
429
|
+
const [, original] = state.lookup(lookupNode, null, nodeStack);
|
|
430
|
+
if (!original) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
const [, current] = state.lookup(lookupNode);
|
|
434
|
+
// For now, leave it alone if it already maps to the same thing.
|
|
435
|
+
// With a bit more work, we could find the guaranteed shortest
|
|
436
|
+
// reference, and then use this to optimize *all* symbols, not
|
|
437
|
+
// just fix inlined ones.
|
|
438
|
+
if (current &&
|
|
439
|
+
current.length === original.length &&
|
|
440
|
+
current.every((item, index) => item == original[index])) {
|
|
441
|
+
return lookupNode;
|
|
442
|
+
}
|
|
443
|
+
const node = lookupNode.type === "Identifier"
|
|
444
|
+
? lookupNode
|
|
445
|
+
: lookupNode.property;
|
|
446
|
+
if (original.length === 1 && original[0].type === "EnumStringMember") {
|
|
447
|
+
return applyTypeIfNeeded(original[0].init);
|
|
448
|
+
}
|
|
449
|
+
const prefixes = original.map((sn) => {
|
|
450
|
+
if (isStateNode(sn) && sn.fullName) {
|
|
451
|
+
return sn.fullName;
|
|
452
|
+
}
|
|
453
|
+
return "";
|
|
454
|
+
});
|
|
455
|
+
if (prefixes.length &&
|
|
456
|
+
prefixes[0].startsWith("$.") &&
|
|
457
|
+
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
458
|
+
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
459
|
+
let found = false;
|
|
460
|
+
return prefix.reduce((current, name) => {
|
|
461
|
+
if (found)
|
|
462
|
+
return current;
|
|
463
|
+
const [, results] = state.lookup(current);
|
|
464
|
+
if (results &&
|
|
465
|
+
results.length === original.length &&
|
|
466
|
+
results.every((result, i) => result === original[i])) {
|
|
467
|
+
found = true;
|
|
468
|
+
return current;
|
|
469
|
+
}
|
|
470
|
+
const object = typeof name === "string"
|
|
471
|
+
? {
|
|
472
|
+
type: "Identifier",
|
|
473
|
+
name,
|
|
474
|
+
start: node.start,
|
|
475
|
+
end: node.end,
|
|
476
|
+
loc: node.loc,
|
|
477
|
+
}
|
|
478
|
+
: name;
|
|
479
|
+
let root = null;
|
|
480
|
+
let property = current;
|
|
481
|
+
while (property.type !== "Identifier") {
|
|
482
|
+
root = property;
|
|
483
|
+
property = property.object;
|
|
484
|
+
}
|
|
485
|
+
const mb = {
|
|
486
|
+
type: "MemberExpression",
|
|
487
|
+
object,
|
|
488
|
+
property,
|
|
489
|
+
computed: false,
|
|
490
|
+
start: node.start,
|
|
491
|
+
end: node.end,
|
|
492
|
+
loc: node.loc,
|
|
493
|
+
};
|
|
494
|
+
if (root) {
|
|
495
|
+
root.object = mb;
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
current = mb;
|
|
499
|
+
}
|
|
500
|
+
return current;
|
|
501
|
+
}, node);
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
|
|
236
506
|
;// CONCATENATED MODULE: external "./util.cjs"
|
|
237
507
|
const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
238
508
|
;// CONCATENATED MODULE: ./src/mc-rewrite.ts
|
|
@@ -240,6 +510,7 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
|
|
|
240
510
|
|
|
241
511
|
|
|
242
512
|
|
|
513
|
+
|
|
243
514
|
function processImports(allImports, lookup) {
|
|
244
515
|
allImports.forEach(({ node, stack }) => {
|
|
245
516
|
const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
|
|
@@ -359,30 +630,34 @@ function getFileASTs(fnMap) {
|
|
|
359
630
|
}, true));
|
|
360
631
|
}
|
|
361
632
|
async function analyze(fnMap) {
|
|
362
|
-
let excludeAnnotations;
|
|
363
633
|
let hasTests = false;
|
|
364
634
|
const allImports = [];
|
|
365
635
|
const preState = {
|
|
636
|
+
fnMap,
|
|
366
637
|
allFunctions: [],
|
|
367
638
|
allClasses: [],
|
|
368
639
|
shouldExclude(node) {
|
|
369
640
|
if ("attrs" in node &&
|
|
370
641
|
node.attrs &&
|
|
371
642
|
"attrs" in node.attrs &&
|
|
372
|
-
node.attrs.attrs
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
643
|
+
node.attrs.attrs &&
|
|
644
|
+
node.loc?.source) {
|
|
645
|
+
const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
|
|
646
|
+
if (excludeAnnotations) {
|
|
647
|
+
return node.attrs.attrs.reduce((drop, attr) => {
|
|
648
|
+
if (attr.type != "UnaryExpression")
|
|
649
|
+
return drop;
|
|
650
|
+
if (attr.argument.type != "Identifier")
|
|
651
|
+
return drop;
|
|
652
|
+
if (hasProperty(excludeAnnotations, attr.argument.name)) {
|
|
653
|
+
return true;
|
|
654
|
+
}
|
|
655
|
+
if (attr.argument.name == "test") {
|
|
656
|
+
hasTests = true;
|
|
657
|
+
}
|
|
377
658
|
return drop;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
if (attr.argument.name == "test") {
|
|
382
|
-
hasTests = true;
|
|
383
|
-
}
|
|
384
|
-
return drop;
|
|
385
|
-
}, false);
|
|
659
|
+
}, false);
|
|
660
|
+
}
|
|
386
661
|
}
|
|
387
662
|
return false;
|
|
388
663
|
},
|
|
@@ -431,7 +706,6 @@ async function analyze(fnMap) {
|
|
|
431
706
|
if (!ast) {
|
|
432
707
|
throw parserError || new Error(`Failed to parse ${name}`);
|
|
433
708
|
}
|
|
434
|
-
excludeAnnotations = value.excludeAnnotations;
|
|
435
709
|
hasTests = false;
|
|
436
710
|
collectNamespaces(ast, state);
|
|
437
711
|
value.hasTests = hasTests;
|
|
@@ -455,8 +729,8 @@ function getLiteralFromDecls(decls) {
|
|
|
455
729
|
let result = null;
|
|
456
730
|
if (decls.every((d) => {
|
|
457
731
|
if (d.type === "EnumStringMember" ||
|
|
458
|
-
(d.type === "VariableDeclarator" && d.kind === "const")) {
|
|
459
|
-
const init = getLiteralNode(d.init);
|
|
732
|
+
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
733
|
+
const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
|
|
460
734
|
if (!init)
|
|
461
735
|
return false;
|
|
462
736
|
if (!result) {
|
|
@@ -986,11 +1260,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
986
1260
|
}
|
|
987
1261
|
return null;
|
|
988
1262
|
}
|
|
989
|
-
if (callees.length == 1) {
|
|
990
|
-
const callee =
|
|
991
|
-
if (callee &&
|
|
992
|
-
callee.type == "FunctionDeclaration" &&
|
|
993
|
-
callee.optimizable &&
|
|
1263
|
+
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
1264
|
+
const callee = callees[0].node;
|
|
1265
|
+
if (callee.optimizable &&
|
|
994
1266
|
!callee.hasOverride &&
|
|
995
1267
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
996
1268
|
const ret = evaluateFunction(callee, node.arguments);
|
|
@@ -999,6 +1271,13 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
999
1271
|
return null;
|
|
1000
1272
|
}
|
|
1001
1273
|
}
|
|
1274
|
+
if (shouldInline(state, callees[0], node.arguments)) {
|
|
1275
|
+
const ret = inlineFunction(state, callees[0], node);
|
|
1276
|
+
if (ret) {
|
|
1277
|
+
replace(node, ret);
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1002
1281
|
}
|
|
1003
1282
|
if (!hasProperty(state.calledFunctions, name)) {
|
|
1004
1283
|
state.calledFunctions[name] = [];
|
|
@@ -1012,6 +1291,8 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
1012
1291
|
Object.values(fnMap).forEach((f) => {
|
|
1013
1292
|
collectNamespaces(f.ast, state);
|
|
1014
1293
|
});
|
|
1294
|
+
delete state.pre;
|
|
1295
|
+
delete state.post;
|
|
1015
1296
|
const cleanup = (node) => {
|
|
1016
1297
|
switch (node.type) {
|
|
1017
1298
|
case "EnumStringBody":
|
|
@@ -1178,13 +1459,14 @@ async function api_getApiMapping(state) {
|
|
|
1178
1459
|
filepath: "api.mir",
|
|
1179
1460
|
}), state);
|
|
1180
1461
|
negativeFixups.forEach((fixup) => {
|
|
1181
|
-
const
|
|
1462
|
+
const vs = fixup.split(".").reduce((state, part) => {
|
|
1182
1463
|
const decls = api_isStateNode(state) && state.decls?.[part];
|
|
1183
1464
|
if (!Array.isArray(decls) || decls.length != 1 || !decls[0]) {
|
|
1184
1465
|
throw `Failed to find and fix negative constant ${fixup}`;
|
|
1185
1466
|
}
|
|
1186
1467
|
return decls[0];
|
|
1187
1468
|
}, result);
|
|
1469
|
+
const value = api_isStateNode(vs) ? vs.node : vs;
|
|
1188
1470
|
if (value.type !== "EnumStringMember" &&
|
|
1189
1471
|
(value.type !== "VariableDeclarator" || value.kind != "const")) {
|
|
1190
1472
|
throw `Negative constant ${fixup} did not refer to a constant`;
|
|
@@ -1221,8 +1503,19 @@ function api_variableDeclarationName(node) {
|
|
|
1221
1503
|
return ("left" in node ? node.left : node).name;
|
|
1222
1504
|
}
|
|
1223
1505
|
function checkOne(ns, decls, name) {
|
|
1224
|
-
if (api_isStateNode(ns)
|
|
1225
|
-
|
|
1506
|
+
if (api_isStateNode(ns)) {
|
|
1507
|
+
if (api_hasProperty(ns[decls], name)) {
|
|
1508
|
+
return ns[decls][name];
|
|
1509
|
+
}
|
|
1510
|
+
if (ns.type == "ClassDeclaration" &&
|
|
1511
|
+
ns.superClass &&
|
|
1512
|
+
ns.superClass !== true) {
|
|
1513
|
+
const found = ns.superClass
|
|
1514
|
+
.map((cls) => checkOne(cls, decls, name))
|
|
1515
|
+
.filter((n) => n != null)
|
|
1516
|
+
.flat(1);
|
|
1517
|
+
return found.length ? found : null;
|
|
1518
|
+
}
|
|
1226
1519
|
}
|
|
1227
1520
|
return null;
|
|
1228
1521
|
}
|
|
@@ -1232,7 +1525,7 @@ function lookup(state, decls, node, name, stack) {
|
|
|
1232
1525
|
case "MemberExpression": {
|
|
1233
1526
|
if (node.property.type != "Identifier" || node.computed)
|
|
1234
1527
|
break;
|
|
1235
|
-
const [, module, where] =
|
|
1528
|
+
const [, module, where] = lookup(state, decls, node.object, name, stack);
|
|
1236
1529
|
if (module && module.length === 1) {
|
|
1237
1530
|
const result = checkOne(module[0], decls, node.property.name);
|
|
1238
1531
|
if (result) {
|
|
@@ -1283,22 +1576,18 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1283
1576
|
state.removeNodeComments = (node, ast) => {
|
|
1284
1577
|
if (node.start && node.end && ast.comments && ast.comments.length) {
|
|
1285
1578
|
let low = 0, high = ast.comments.length;
|
|
1286
|
-
while (
|
|
1579
|
+
while (high > low) {
|
|
1287
1580
|
const mid = (low + high) >> 1;
|
|
1288
|
-
if (mid == low) {
|
|
1289
|
-
if (ast.comments[mid].start < node.start) {
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
break;
|
|
1293
|
-
}
|
|
1294
1581
|
if (ast.comments[mid].start < node.start) {
|
|
1295
|
-
low = mid;
|
|
1582
|
+
low = mid + 1;
|
|
1296
1583
|
}
|
|
1297
1584
|
else {
|
|
1298
1585
|
high = mid;
|
|
1299
1586
|
}
|
|
1300
1587
|
}
|
|
1301
|
-
|
|
1588
|
+
while (high < ast.comments.length && ast.comments[high].end < node.end) {
|
|
1589
|
+
high++;
|
|
1590
|
+
}
|
|
1302
1591
|
if (high > low) {
|
|
1303
1592
|
ast.comments.splice(low, high - low);
|
|
1304
1593
|
}
|
|
@@ -1401,7 +1690,17 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1401
1690
|
if (!api_hasProperty(parent.type_decls, name)) {
|
|
1402
1691
|
parent.type_decls[name] = [];
|
|
1403
1692
|
}
|
|
1404
|
-
(
|
|
1693
|
+
else if (parent.type_decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == node)) {
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
parent.type_decls[name].push(node.type === "EnumDeclaration"
|
|
1697
|
+
? node
|
|
1698
|
+
: {
|
|
1699
|
+
type: "TypedefDeclaration",
|
|
1700
|
+
node,
|
|
1701
|
+
name,
|
|
1702
|
+
fullName: parent.fullName + "." + name,
|
|
1703
|
+
});
|
|
1405
1704
|
break;
|
|
1406
1705
|
}
|
|
1407
1706
|
case "VariableDeclaration": {
|
|
@@ -1409,13 +1708,23 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1409
1708
|
if (!parent.decls)
|
|
1410
1709
|
parent.decls = {};
|
|
1411
1710
|
const decls = parent.decls;
|
|
1711
|
+
const stack = state.stack.slice();
|
|
1412
1712
|
node.declarations.forEach((decl) => {
|
|
1413
1713
|
const name = api_variableDeclarationName(decl.id);
|
|
1414
1714
|
if (!api_hasProperty(decls, name)) {
|
|
1415
1715
|
decls[name] = [];
|
|
1416
1716
|
}
|
|
1717
|
+
else if (decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == decl)) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1417
1720
|
decl.kind = node.kind;
|
|
1418
|
-
(0,external_util_cjs_namespaceObject.pushUnique)(decls[name],
|
|
1721
|
+
(0,external_util_cjs_namespaceObject.pushUnique)(decls[name], {
|
|
1722
|
+
type: "VariableDeclarator",
|
|
1723
|
+
node: decl,
|
|
1724
|
+
name,
|
|
1725
|
+
fullName: parent.fullName + "." + name,
|
|
1726
|
+
stack,
|
|
1727
|
+
});
|
|
1419
1728
|
if (node.kind == "const") {
|
|
1420
1729
|
if (!api_hasProperty(state.index, name)) {
|
|
1421
1730
|
state.index[name] = [];
|
package/build/optimizer.cjs
CHANGED
|
@@ -10791,11 +10791,282 @@ 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/inliner.ts
|
|
10795
|
+
|
|
10796
|
+
function canInline(state, func, args) {
|
|
10797
|
+
// determine whether decl might be changed by a function call
|
|
10798
|
+
// during the evaluation of FunctionStateNode.
|
|
10799
|
+
const getSafety = (decl) => {
|
|
10800
|
+
// enums are constant, they cant change
|
|
10801
|
+
if (decl.type === "EnumStringMember")
|
|
10802
|
+
return true;
|
|
10803
|
+
if (decl.type === "VariableDeclarator") {
|
|
10804
|
+
// constants also can't change
|
|
10805
|
+
if (decl.node.kind === "const")
|
|
10806
|
+
return true;
|
|
10807
|
+
// if decl is a local, it also can't be changed
|
|
10808
|
+
// by a call to another function.
|
|
10809
|
+
for (let i = 0;; i++) {
|
|
10810
|
+
if (!state.stack[i] || decl.stack[i] !== state.stack[i])
|
|
10811
|
+
return false;
|
|
10812
|
+
if (state.stack[i].type === "FunctionDeclaration")
|
|
10813
|
+
return true;
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
return null;
|
|
10817
|
+
};
|
|
10818
|
+
const safeArgs = [];
|
|
10819
|
+
let allSafe = true;
|
|
10820
|
+
if (!args.every((arg) => {
|
|
10821
|
+
switch (arg.type) {
|
|
10822
|
+
case "Literal":
|
|
10823
|
+
safeArgs.push(true);
|
|
10824
|
+
return true;
|
|
10825
|
+
case "Identifier":
|
|
10826
|
+
case "MemberExpression": {
|
|
10827
|
+
const [, results] = state.lookup(arg);
|
|
10828
|
+
if (!results || results.length !== 1)
|
|
10829
|
+
return false;
|
|
10830
|
+
const safety = getSafety(results[0]);
|
|
10831
|
+
if (safety === null)
|
|
10832
|
+
return false;
|
|
10833
|
+
if (!safety)
|
|
10834
|
+
allSafe = false;
|
|
10835
|
+
safeArgs.push(safety);
|
|
10836
|
+
return true;
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
return false;
|
|
10840
|
+
})) {
|
|
10841
|
+
return false;
|
|
10842
|
+
}
|
|
10843
|
+
if (allSafe)
|
|
10844
|
+
return true;
|
|
10845
|
+
let callSeen = false;
|
|
10846
|
+
let ok = true;
|
|
10847
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
|
|
10848
|
+
const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
|
|
10849
|
+
// look for uses of "unsafe" args that occur after a call.
|
|
10850
|
+
// use post to do the checking, because arguments are evaluated
|
|
10851
|
+
// prior to the call, so eg "return f(x.y);" is fine, but
|
|
10852
|
+
// "return f()+x.y" is not.
|
|
10853
|
+
//
|
|
10854
|
+
// We also have to use a "pre" to ensure that child nodes are
|
|
10855
|
+
// visited in source order (otherwise we could visit x.y before f()
|
|
10856
|
+
// in the above example)
|
|
10857
|
+
(0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
|
|
10858
|
+
return Object.entries(node)
|
|
10859
|
+
.filter((kv) => Array.isArray(kv[1])
|
|
10860
|
+
? kv[1].length !== 0 && (0,external_api_cjs_namespaceObject.hasProperty)(kv[1][0], "type")
|
|
10861
|
+
: (0,external_api_cjs_namespaceObject.hasProperty)(kv[1], "type"))
|
|
10862
|
+
.sort(([, a], [, b]) => getLoc(a) - getLoc(b))
|
|
10863
|
+
.map(([key]) => key);
|
|
10864
|
+
}, (node) => {
|
|
10865
|
+
switch (node.type) {
|
|
10866
|
+
case "CallExpression":
|
|
10867
|
+
case "NewExpression":
|
|
10868
|
+
callSeen = true;
|
|
10869
|
+
break;
|
|
10870
|
+
case "Identifier":
|
|
10871
|
+
if (callSeen &&
|
|
10872
|
+
(0,external_api_cjs_namespaceObject.hasProperty)(params, node.name) &&
|
|
10873
|
+
!safeArgs[params[node.name]]) {
|
|
10874
|
+
ok = false;
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
});
|
|
10878
|
+
return ok;
|
|
10879
|
+
}
|
|
10880
|
+
function inliningLooksUseful(func, node) {
|
|
10881
|
+
while (true) {
|
|
10882
|
+
if (node.type === "BinaryExpression" && node.operator === "as") {
|
|
10883
|
+
node = node.left;
|
|
10884
|
+
}
|
|
10885
|
+
else if (node.type === "UnaryExpression" && node.operator === " as") {
|
|
10886
|
+
node = node.argument;
|
|
10887
|
+
}
|
|
10888
|
+
else {
|
|
10889
|
+
break;
|
|
10890
|
+
}
|
|
10891
|
+
}
|
|
10892
|
+
if (node.type === "Literal")
|
|
10893
|
+
return true;
|
|
10894
|
+
if (node.type === "Identifier") {
|
|
10895
|
+
if (func.params.length === 1 &&
|
|
10896
|
+
(0,external_api_cjs_namespaceObject.variableDeclarationName)(func.params[0]) === node.name) {
|
|
10897
|
+
return 1;
|
|
10898
|
+
}
|
|
10899
|
+
return true;
|
|
10900
|
+
}
|
|
10901
|
+
return false;
|
|
10902
|
+
}
|
|
10903
|
+
function shouldInline(state, func, args) {
|
|
10904
|
+
if (!func.node.body ||
|
|
10905
|
+
func.node.body.body.length !== 1 ||
|
|
10906
|
+
func.node.body.body[0].type !== "ReturnStatement" ||
|
|
10907
|
+
!func.node.body.body[0].argument ||
|
|
10908
|
+
func.node.params.length !== args.length) {
|
|
10909
|
+
return false;
|
|
10910
|
+
}
|
|
10911
|
+
const autoInline = inliningLooksUseful(func.node, func.node.body.body[0].argument);
|
|
10912
|
+
const excludeAnnotations = (func.node.loc?.source &&
|
|
10913
|
+
state.fnMap[func.node.loc?.source].excludeAnnotations) ||
|
|
10914
|
+
{};
|
|
10915
|
+
return ((autoInline ||
|
|
10916
|
+
(func.node.attrs &&
|
|
10917
|
+
func.node.attrs.attrs &&
|
|
10918
|
+
func.node.attrs.attrs.some((attr) => attr.type === "UnaryExpression" &&
|
|
10919
|
+
(attr.argument.name === "inline" ||
|
|
10920
|
+
(attr.argument.name.startsWith("inline_") &&
|
|
10921
|
+
(0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name.substring(7))))))) &&
|
|
10922
|
+
(autoInline === 1 || canInline(state, func, args)));
|
|
10923
|
+
}
|
|
10924
|
+
function inlineFunction(state, func, call) {
|
|
10925
|
+
const retArg = JSON.parse(JSON.stringify(func.node.body.body[0].argument));
|
|
10926
|
+
const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
|
|
10927
|
+
try {
|
|
10928
|
+
const result = (0,external_api_cjs_namespaceObject.traverseAst)(retArg, (node) => {
|
|
10929
|
+
switch (node.type) {
|
|
10930
|
+
case "MemberExpression":
|
|
10931
|
+
if (!node.computed) {
|
|
10932
|
+
return ["object"];
|
|
10933
|
+
}
|
|
10934
|
+
break;
|
|
10935
|
+
case "BinaryExpression":
|
|
10936
|
+
if (node.operator === "as") {
|
|
10937
|
+
return ["left"];
|
|
10938
|
+
}
|
|
10939
|
+
break;
|
|
10940
|
+
case "UnaryExpression":
|
|
10941
|
+
if (node.operator === " as") {
|
|
10942
|
+
return [];
|
|
10943
|
+
}
|
|
10944
|
+
}
|
|
10945
|
+
return null;
|
|
10946
|
+
}, (node) => {
|
|
10947
|
+
switch (node.type) {
|
|
10948
|
+
case "Identifier": {
|
|
10949
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(params, node.name)) {
|
|
10950
|
+
return call.arguments[params[node.name]];
|
|
10951
|
+
}
|
|
10952
|
+
const rep = fixNodeScope(state, node, func.stack);
|
|
10953
|
+
if (!rep) {
|
|
10954
|
+
throw new Error(`Inliner: Couldn't fix the scope of '${node.name}`);
|
|
10955
|
+
}
|
|
10956
|
+
return rep;
|
|
10957
|
+
}
|
|
10958
|
+
}
|
|
10959
|
+
return null;
|
|
10960
|
+
}) || retArg;
|
|
10961
|
+
result.loc = call.loc;
|
|
10962
|
+
result.start = call.start;
|
|
10963
|
+
result.end = call.end;
|
|
10964
|
+
return result;
|
|
10965
|
+
}
|
|
10966
|
+
catch (ex) {
|
|
10967
|
+
if (ex instanceof Error) {
|
|
10968
|
+
if (ex.message.startsWith("Inliner: ")) {
|
|
10969
|
+
return null;
|
|
10970
|
+
}
|
|
10971
|
+
}
|
|
10972
|
+
throw ex;
|
|
10973
|
+
}
|
|
10974
|
+
}
|
|
10975
|
+
function applyTypeIfNeeded(node) {
|
|
10976
|
+
if ("enumType" in node && node.enumType) {
|
|
10977
|
+
node = {
|
|
10978
|
+
type: "BinaryExpression",
|
|
10979
|
+
operator: "as",
|
|
10980
|
+
left: node,
|
|
10981
|
+
right: { type: "TypeSpecList", ts: [node.enumType] },
|
|
10982
|
+
};
|
|
10983
|
+
}
|
|
10984
|
+
return node;
|
|
10985
|
+
}
|
|
10986
|
+
function fixNodeScope(state, lookupNode, nodeStack) {
|
|
10987
|
+
const [, original] = state.lookup(lookupNode, null, nodeStack);
|
|
10988
|
+
if (!original) {
|
|
10989
|
+
return null;
|
|
10990
|
+
}
|
|
10991
|
+
const [, current] = state.lookup(lookupNode);
|
|
10992
|
+
// For now, leave it alone if it already maps to the same thing.
|
|
10993
|
+
// With a bit more work, we could find the guaranteed shortest
|
|
10994
|
+
// reference, and then use this to optimize *all* symbols, not
|
|
10995
|
+
// just fix inlined ones.
|
|
10996
|
+
if (current &&
|
|
10997
|
+
current.length === original.length &&
|
|
10998
|
+
current.every((item, index) => item == original[index])) {
|
|
10999
|
+
return lookupNode;
|
|
11000
|
+
}
|
|
11001
|
+
const node = lookupNode.type === "Identifier"
|
|
11002
|
+
? lookupNode
|
|
11003
|
+
: lookupNode.property;
|
|
11004
|
+
if (original.length === 1 && original[0].type === "EnumStringMember") {
|
|
11005
|
+
return applyTypeIfNeeded(original[0].init);
|
|
11006
|
+
}
|
|
11007
|
+
const prefixes = original.map((sn) => {
|
|
11008
|
+
if ((0,external_api_cjs_namespaceObject.isStateNode)(sn) && sn.fullName) {
|
|
11009
|
+
return sn.fullName;
|
|
11010
|
+
}
|
|
11011
|
+
return "";
|
|
11012
|
+
});
|
|
11013
|
+
if (prefixes.length &&
|
|
11014
|
+
prefixes[0].startsWith("$.") &&
|
|
11015
|
+
prefixes.every((prefix, i) => !i || prefix === prefixes[i - 1])) {
|
|
11016
|
+
const prefix = prefixes[0].split(".").slice(0, -1).reverse();
|
|
11017
|
+
let found = false;
|
|
11018
|
+
return prefix.reduce((current, name) => {
|
|
11019
|
+
if (found)
|
|
11020
|
+
return current;
|
|
11021
|
+
const [, results] = state.lookup(current);
|
|
11022
|
+
if (results &&
|
|
11023
|
+
results.length === original.length &&
|
|
11024
|
+
results.every((result, i) => result === original[i])) {
|
|
11025
|
+
found = true;
|
|
11026
|
+
return current;
|
|
11027
|
+
}
|
|
11028
|
+
const object = typeof name === "string"
|
|
11029
|
+
? {
|
|
11030
|
+
type: "Identifier",
|
|
11031
|
+
name,
|
|
11032
|
+
start: node.start,
|
|
11033
|
+
end: node.end,
|
|
11034
|
+
loc: node.loc,
|
|
11035
|
+
}
|
|
11036
|
+
: name;
|
|
11037
|
+
let root = null;
|
|
11038
|
+
let property = current;
|
|
11039
|
+
while (property.type !== "Identifier") {
|
|
11040
|
+
root = property;
|
|
11041
|
+
property = property.object;
|
|
11042
|
+
}
|
|
11043
|
+
const mb = {
|
|
11044
|
+
type: "MemberExpression",
|
|
11045
|
+
object,
|
|
11046
|
+
property,
|
|
11047
|
+
computed: false,
|
|
11048
|
+
start: node.start,
|
|
11049
|
+
end: node.end,
|
|
11050
|
+
loc: node.loc,
|
|
11051
|
+
};
|
|
11052
|
+
if (root) {
|
|
11053
|
+
root.object = mb;
|
|
11054
|
+
}
|
|
11055
|
+
else {
|
|
11056
|
+
current = mb;
|
|
11057
|
+
}
|
|
11058
|
+
return current;
|
|
11059
|
+
}, node);
|
|
11060
|
+
}
|
|
11061
|
+
return null;
|
|
11062
|
+
}
|
|
11063
|
+
|
|
10794
11064
|
;// CONCATENATED MODULE: ./src/mc-rewrite.ts
|
|
10795
11065
|
|
|
10796
11066
|
|
|
10797
11067
|
|
|
10798
11068
|
|
|
11069
|
+
|
|
10799
11070
|
function processImports(allImports, lookup) {
|
|
10800
11071
|
allImports.forEach(({ node, stack }) => {
|
|
10801
11072
|
const [name, module] = lookup(node.id, ("as" in node && node.as && node.as.name) || null, stack);
|
|
@@ -10914,30 +11185,34 @@ function getFileASTs(fnMap) {
|
|
|
10914
11185
|
}, true));
|
|
10915
11186
|
}
|
|
10916
11187
|
async function analyze(fnMap) {
|
|
10917
|
-
let excludeAnnotations;
|
|
10918
11188
|
let hasTests = false;
|
|
10919
11189
|
const allImports = [];
|
|
10920
11190
|
const preState = {
|
|
11191
|
+
fnMap,
|
|
10921
11192
|
allFunctions: [],
|
|
10922
11193
|
allClasses: [],
|
|
10923
11194
|
shouldExclude(node) {
|
|
10924
11195
|
if ("attrs" in node &&
|
|
10925
11196
|
node.attrs &&
|
|
10926
11197
|
"attrs" in node.attrs &&
|
|
10927
|
-
node.attrs.attrs
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
11198
|
+
node.attrs.attrs &&
|
|
11199
|
+
node.loc?.source) {
|
|
11200
|
+
const excludeAnnotations = fnMap[node.loc.source].excludeAnnotations;
|
|
11201
|
+
if (excludeAnnotations) {
|
|
11202
|
+
return node.attrs.attrs.reduce((drop, attr) => {
|
|
11203
|
+
if (attr.type != "UnaryExpression")
|
|
11204
|
+
return drop;
|
|
11205
|
+
if (attr.argument.type != "Identifier")
|
|
11206
|
+
return drop;
|
|
11207
|
+
if ((0,external_api_cjs_namespaceObject.hasProperty)(excludeAnnotations, attr.argument.name)) {
|
|
11208
|
+
return true;
|
|
11209
|
+
}
|
|
11210
|
+
if (attr.argument.name == "test") {
|
|
11211
|
+
hasTests = true;
|
|
11212
|
+
}
|
|
10932
11213
|
return drop;
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
}
|
|
10936
|
-
if (attr.argument.name == "test") {
|
|
10937
|
-
hasTests = true;
|
|
10938
|
-
}
|
|
10939
|
-
return drop;
|
|
10940
|
-
}, false);
|
|
11214
|
+
}, false);
|
|
11215
|
+
}
|
|
10941
11216
|
}
|
|
10942
11217
|
return false;
|
|
10943
11218
|
},
|
|
@@ -10986,7 +11261,6 @@ async function analyze(fnMap) {
|
|
|
10986
11261
|
if (!ast) {
|
|
10987
11262
|
throw parserError || new Error(`Failed to parse ${name}`);
|
|
10988
11263
|
}
|
|
10989
|
-
excludeAnnotations = value.excludeAnnotations;
|
|
10990
11264
|
hasTests = false;
|
|
10991
11265
|
(0,external_api_cjs_namespaceObject.collectNamespaces)(ast, state);
|
|
10992
11266
|
value.hasTests = hasTests;
|
|
@@ -11010,8 +11284,8 @@ function getLiteralFromDecls(decls) {
|
|
|
11010
11284
|
let result = null;
|
|
11011
11285
|
if (decls.every((d) => {
|
|
11012
11286
|
if (d.type === "EnumStringMember" ||
|
|
11013
|
-
(d.type === "VariableDeclarator" && d.kind === "const")) {
|
|
11014
|
-
const init = getLiteralNode(d.init);
|
|
11287
|
+
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
11288
|
+
const init = getLiteralNode(d.type === "EnumStringMember" ? d.init : d.node.init);
|
|
11015
11289
|
if (!init)
|
|
11016
11290
|
return false;
|
|
11017
11291
|
if (!result) {
|
|
@@ -11541,11 +11815,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
11541
11815
|
}
|
|
11542
11816
|
return null;
|
|
11543
11817
|
}
|
|
11544
|
-
if (callees.length == 1) {
|
|
11545
|
-
const callee =
|
|
11546
|
-
if (callee &&
|
|
11547
|
-
callee.type == "FunctionDeclaration" &&
|
|
11548
|
-
callee.optimizable &&
|
|
11818
|
+
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
11819
|
+
const callee = callees[0].node;
|
|
11820
|
+
if (callee.optimizable &&
|
|
11549
11821
|
!callee.hasOverride &&
|
|
11550
11822
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
11551
11823
|
const ret = evaluateFunction(callee, node.arguments);
|
|
@@ -11554,6 +11826,13 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
11554
11826
|
return null;
|
|
11555
11827
|
}
|
|
11556
11828
|
}
|
|
11829
|
+
if (shouldInline(state, callees[0], node.arguments)) {
|
|
11830
|
+
const ret = inlineFunction(state, callees[0], node);
|
|
11831
|
+
if (ret) {
|
|
11832
|
+
replace(node, ret);
|
|
11833
|
+
return null;
|
|
11834
|
+
}
|
|
11835
|
+
}
|
|
11557
11836
|
}
|
|
11558
11837
|
if (!(0,external_api_cjs_namespaceObject.hasProperty)(state.calledFunctions, name)) {
|
|
11559
11838
|
state.calledFunctions[name] = [];
|
|
@@ -11567,6 +11846,8 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
11567
11846
|
Object.values(fnMap).forEach((f) => {
|
|
11568
11847
|
(0,external_api_cjs_namespaceObject.collectNamespaces)(f.ast, state);
|
|
11569
11848
|
});
|
|
11849
|
+
delete state.pre;
|
|
11850
|
+
delete state.post;
|
|
11570
11851
|
const cleanup = (node) => {
|
|
11571
11852
|
switch (node.type) {
|
|
11572
11853
|
case "EnumStringBody":
|
|
@@ -12054,6 +12335,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
|
|
|
12054
12335
|
// might have altered it), and that the options we care about haven't
|
|
12055
12336
|
// changed
|
|
12056
12337
|
if (hasTests != null &&
|
|
12338
|
+
!config.checkBuildPragmas &&
|
|
12057
12339
|
configOptionsToCheck.every((option) => prevOptions[option] === config[option]) &&
|
|
12058
12340
|
actualOptimizedFiles.length == Object.values(fnMap).length &&
|
|
12059
12341
|
Object.values(fnMap)
|
|
@@ -12064,7 +12346,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
|
|
|
12064
12346
|
// the oldest optimized file, we don't need to regenerate
|
|
12065
12347
|
const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
|
|
12066
12348
|
const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
|
|
12067
|
-
if (source_time < opt_time &&
|
|
12349
|
+
if (source_time < opt_time && 1654222986123 < opt_time) {
|
|
12068
12350
|
return hasTests;
|
|
12069
12351
|
}
|
|
12070
12352
|
}
|
|
@@ -12076,6 +12358,14 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
|
|
|
12076
12358
|
const dir = external_path_.dirname(name);
|
|
12077
12359
|
await promises_namespaceObject.mkdir(dir, { recursive: true });
|
|
12078
12360
|
const opt_source = (0,external_api_cjs_namespaceObject.formatAst)(info.ast, info.monkeyCSource);
|
|
12361
|
+
if (config.checkBuildPragmas) {
|
|
12362
|
+
const matches = opt_source.matchAll(/^.*\/\*\s*@match\s+(\S+)\s+\*\/(.*)$/gm);
|
|
12363
|
+
for (const [line, needle, haystack] of matches) {
|
|
12364
|
+
if (!haystack.includes(needle)) {
|
|
12365
|
+
throw new Error(`Checking build pragmas in ${name} failed at \n\n${line}\n\n - Didn't find '${needle}'`);
|
|
12366
|
+
}
|
|
12367
|
+
}
|
|
12368
|
+
}
|
|
12079
12369
|
await promises_namespaceObject.writeFile(name, opt_source);
|
|
12080
12370
|
return info.hasTests;
|
|
12081
12371
|
})).then((results) => {
|
|
@@ -12133,11 +12423,12 @@ async function generateApiMirTests(options) {
|
|
|
12133
12423
|
return;
|
|
12134
12424
|
const d = decl[0];
|
|
12135
12425
|
if (d.type === "EnumStringMember" ||
|
|
12136
|
-
(d.type === "VariableDeclarator" && d.kind === "const")) {
|
|
12137
|
-
|
|
12426
|
+
(d.type === "VariableDeclarator" && d.node.kind === "const")) {
|
|
12427
|
+
const init = (0,external_api_cjs_namespaceObject.isStateNode)(d) ? d.node.init : d.init;
|
|
12428
|
+
if (!init) {
|
|
12138
12429
|
throw new Error(`Missing init for ${node.fullName}.${key}`);
|
|
12139
12430
|
}
|
|
12140
|
-
tests.push([`${node.fullName}.${key}`, (0,external_api_cjs_namespaceObject.formatAst)(
|
|
12431
|
+
tests.push([`${node.fullName}.${key}`, (0,external_api_cjs_namespaceObject.formatAst)(init)]);
|
|
12141
12432
|
}
|
|
12142
12433
|
else if ((0,external_api_cjs_namespaceObject.isStateNode)(d)) {
|
|
12143
12434
|
findConstants(d);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { mctree } from "@markw65/prettier-plugin-monkeyc";
|
|
2
|
+
export declare function shouldInline(state: ProgramStateAnalysis, func: FunctionStateNode, args: mctree.Node[]): boolean | undefined;
|
|
3
|
+
export declare function inlineFunction(state: ProgramStateAnalysis, func: FunctionStateNode, call: mctree.CallExpression): any;
|
|
4
|
+
export declare function applyTypeIfNeeded(node: mctree.Node): mctree.Node;
|
package/build/src/optimizer.d.ts
CHANGED
|
@@ -34,12 +34,13 @@ declare global {
|
|
|
34
34
|
ignoredAnnotations?: string;
|
|
35
35
|
ignoredSourcePaths?: string;
|
|
36
36
|
returnCommand?: boolean;
|
|
37
|
+
checkBuildPragmas?: boolean;
|
|
37
38
|
_cache?: {
|
|
38
39
|
barrels?: Record<string, ResolvedJungle>;
|
|
39
40
|
barrelMap?: Record<string, Record<string, ResolvedJungle>>;
|
|
40
41
|
};
|
|
41
42
|
};
|
|
42
|
-
type StateNodeDecl = StateNode | mctree.EnumStringMember | mctree.TypedIdentifier | mctree.EnumDeclaration
|
|
43
|
+
type StateNodeDecl = StateNode | mctree.EnumStringMember | mctree.TypedIdentifier | mctree.EnumDeclaration;
|
|
43
44
|
type StateNodeDecls = {
|
|
44
45
|
[key: string]: StateNodeDecl[];
|
|
45
46
|
};
|
|
@@ -87,11 +88,25 @@ declare global {
|
|
|
87
88
|
node: mctree.BlockStatement;
|
|
88
89
|
stack?: undefined;
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
+
interface TypedefStateNode extends BaseStateNode {
|
|
92
|
+
type: "TypedefDeclaration";
|
|
93
|
+
node: mctree.TypedefDeclaration;
|
|
94
|
+
name: string;
|
|
95
|
+
fullName: string;
|
|
96
|
+
}
|
|
97
|
+
interface VariableStateNode extends BaseStateNode {
|
|
98
|
+
type: "VariableDeclarator";
|
|
99
|
+
node: mctree.VariableDeclarator;
|
|
100
|
+
name: string;
|
|
101
|
+
fullName: string;
|
|
102
|
+
stack: ProgramStateStack;
|
|
103
|
+
}
|
|
104
|
+
type StateNode = ProgramStateNode | FunctionStateNode | BlockStateNode | ClassStateNode | ModuleStateNode | TypedefStateNode | VariableStateNode;
|
|
91
105
|
type ProgramStateStack = StateNode[];
|
|
92
106
|
export type ProgramState = {
|
|
93
107
|
allFunctions?: FunctionStateNode[];
|
|
94
108
|
allClasses?: ClassStateNode[];
|
|
109
|
+
fnMap?: FilesToOptimizeMap;
|
|
95
110
|
stack?: ProgramStateStack;
|
|
96
111
|
removeNodeComments?: (node: mctree.Node, ast: mctree.Program) => void;
|
|
97
112
|
shouldExclude?: (node: mctree.Node) => boolean;
|
|
@@ -128,7 +143,7 @@ declare global {
|
|
|
128
143
|
[key in Keys]-?: NonNullable<T[key]>;
|
|
129
144
|
};
|
|
130
145
|
export type ProgramStateLive = Finalized<ProgramState, "stack" | "lookup" | "lookupValue" | "lookupType" | "traverse" | "index" | "constants" | "removeNodeComments" | "inType">;
|
|
131
|
-
export type ProgramStateAnalysis = Finalized<ProgramStateLive, "allClasses" | "allFunctions">;
|
|
146
|
+
export type ProgramStateAnalysis = Finalized<ProgramStateLive, "allClasses" | "allFunctions" | "fnMap">;
|
|
132
147
|
export type ProgramStateOptimizer = Finalized<ProgramStateAnalysis, "localsStack" | "exposed" | "calledFunctions">;
|
|
133
148
|
type ExcludeAnnotationsMap = {
|
|
134
149
|
[key: string]: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markw65/monkeyc-optimizer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.16",
|
|
5
5
|
"description": "Source to source optimizer for Garmin Monkey C code",
|
|
6
6
|
"main": "build/optimizer.cjs",
|
|
7
7
|
"types": "build/src/optimizer.d.ts",
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
"build-debug": "webpack --mode development",
|
|
21
21
|
"build-release": "webpack --mode production",
|
|
22
22
|
"prepack": "webpack --mode production",
|
|
23
|
-
"test": "npm run test-remote",
|
|
24
|
-
"test-remote": "node ./test/test.js --product=pick-one --github"
|
|
23
|
+
"test": "npm run test-inline && npm run test-remote",
|
|
24
|
+
"test-remote": "node ./test/test.js --product=pick-one --github",
|
|
25
|
+
"test-inline": "node test/test.js --run-tests --product=fenix5 --product=fr235 --jungle $(pwd)/test/OptimizerTests/monkey.jungle"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
28
|
"build/optimizer.cjs",
|