@markw65/monkeyc-optimizer 1.0.13 → 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 +37 -0
- package/build/api.cjs +573 -155
- package/build/optimizer.cjs +584 -235
- package/build/sdk-util.cjs +8 -2
- package/build/src/inliner.d.ts +4 -0
- package/build/src/optimizer.d.ts +52 -30
- package/build/src/sdk-util.d.ts +4 -2
- package/build/util.cjs +12 -4
- package/package.json +13 -6
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);
|
|
@@ -248,11 +519,21 @@ function processImports(allImports, lookup) {
|
|
|
248
519
|
if (!parent.decls)
|
|
249
520
|
parent.decls = {};
|
|
250
521
|
const decls = parent.decls;
|
|
251
|
-
if (!hasProperty(
|
|
252
|
-
|
|
522
|
+
if (!hasProperty(decls, name))
|
|
523
|
+
decls[name] = [];
|
|
253
524
|
module.forEach((m) => {
|
|
254
525
|
if (isStateNode(m) && m.type == "ModuleDeclaration") {
|
|
255
526
|
pushUnique(decls[name], m);
|
|
527
|
+
if (node.type == "ImportModule" && m.type_decls) {
|
|
528
|
+
if (!parent.type_decls)
|
|
529
|
+
parent.type_decls = {};
|
|
530
|
+
const tdecls = parent.type_decls;
|
|
531
|
+
Object.entries(m.type_decls).forEach(([name, decls]) => {
|
|
532
|
+
if (!hasProperty(tdecls, name))
|
|
533
|
+
tdecls[name] = [];
|
|
534
|
+
decls.forEach((decl) => pushUnique(tdecls[name], decl));
|
|
535
|
+
});
|
|
536
|
+
}
|
|
256
537
|
}
|
|
257
538
|
});
|
|
258
539
|
}
|
|
@@ -261,11 +542,39 @@ function processImports(allImports, lookup) {
|
|
|
261
542
|
function collectClassInfo(state) {
|
|
262
543
|
state.allClasses.forEach((elm) => {
|
|
263
544
|
if (elm.node.superClass) {
|
|
264
|
-
const [, classes] = state.lookup(elm.node.superClass, null, elm.stack);
|
|
545
|
+
const [name, classes] = state.lookup(elm.node.superClass, null, elm.stack);
|
|
265
546
|
const superClass = classes &&
|
|
266
547
|
classes.filter((c) => isStateNode(c) && c.type === "ClassDeclaration");
|
|
267
548
|
// set it "true" if there is a superClass, but we can't find it.
|
|
268
549
|
elm.superClass = superClass && superClass.length ? superClass : true;
|
|
550
|
+
if (name && elm.superClass !== true) {
|
|
551
|
+
/*
|
|
552
|
+
* The runtime behavior of monkeyc is strange. Lookups
|
|
553
|
+
* of the name of the superclass, either bare, or via self.<name>
|
|
554
|
+
* always find the superclass, even if there's a member variable
|
|
555
|
+
* or method of the same name. So its ok to just overwrite
|
|
556
|
+
* elm.decls[name] here.
|
|
557
|
+
*
|
|
558
|
+
* ie
|
|
559
|
+
*
|
|
560
|
+
* class A { function foo() as Number { return 1; } }
|
|
561
|
+
* class B { function foo() as String { return "B"; } }
|
|
562
|
+
* class C extends A {
|
|
563
|
+
* var A as B = new B();
|
|
564
|
+
* function initialize() {
|
|
565
|
+
* A.initialize(); // class A's initialize
|
|
566
|
+
* A.foo(); // returns 1
|
|
567
|
+
* self.A.foo(); // still returns 1
|
|
568
|
+
* }
|
|
569
|
+
* }
|
|
570
|
+
*
|
|
571
|
+
* The typechecker seems to get confused in some circumstances
|
|
572
|
+
* though (ie it doesn't always use the same rules)
|
|
573
|
+
*/
|
|
574
|
+
if (!elm.decls)
|
|
575
|
+
elm.decls = {};
|
|
576
|
+
elm.decls[name] = elm.superClass;
|
|
577
|
+
}
|
|
269
578
|
}
|
|
270
579
|
});
|
|
271
580
|
const markOverrides = (cls, scls) => {
|
|
@@ -321,32 +630,36 @@ function getFileASTs(fnMap) {
|
|
|
321
630
|
}, true));
|
|
322
631
|
}
|
|
323
632
|
async function analyze(fnMap) {
|
|
324
|
-
let excludeAnnotations;
|
|
325
633
|
let hasTests = false;
|
|
326
634
|
const allImports = [];
|
|
327
635
|
const preState = {
|
|
636
|
+
fnMap,
|
|
328
637
|
allFunctions: [],
|
|
329
638
|
allClasses: [],
|
|
330
639
|
shouldExclude(node) {
|
|
331
640
|
if ("attrs" in node &&
|
|
332
641
|
node.attrs &&
|
|
333
642
|
"attrs" in node.attrs &&
|
|
334
|
-
node.attrs.attrs
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
+
}
|
|
339
658
|
return drop;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
if (attr.argument.name == "test") {
|
|
344
|
-
hasTests = true;
|
|
345
|
-
}
|
|
346
|
-
return drop;
|
|
347
|
-
}, false);
|
|
659
|
+
}, false);
|
|
660
|
+
}
|
|
348
661
|
}
|
|
349
|
-
return
|
|
662
|
+
return false;
|
|
350
663
|
},
|
|
351
664
|
post(node, state) {
|
|
352
665
|
switch (node.type) {
|
|
@@ -393,7 +706,6 @@ async function analyze(fnMap) {
|
|
|
393
706
|
if (!ast) {
|
|
394
707
|
throw parserError || new Error(`Failed to parse ${name}`);
|
|
395
708
|
}
|
|
396
|
-
excludeAnnotations = value.excludeAnnotations;
|
|
397
709
|
hasTests = false;
|
|
398
710
|
collectNamespaces(ast, state);
|
|
399
711
|
value.hasTests = hasTests;
|
|
@@ -417,8 +729,8 @@ function getLiteralFromDecls(decls) {
|
|
|
417
729
|
let result = null;
|
|
418
730
|
if (decls.every((d) => {
|
|
419
731
|
if (d.type === "EnumStringMember" ||
|
|
420
|
-
(d.type === "VariableDeclarator" && d.kind === "const")) {
|
|
421
|
-
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);
|
|
422
734
|
if (!init)
|
|
423
735
|
return false;
|
|
424
736
|
if (!result) {
|
|
@@ -700,11 +1012,12 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
700
1012
|
* anywhere in its superClass chain.
|
|
701
1013
|
*/
|
|
702
1014
|
const checkInherited = (elm, name) => elm.superClass === true ||
|
|
703
|
-
elm.superClass
|
|
704
|
-
|
|
705
|
-
f
|
|
706
|
-
|
|
707
|
-
|
|
1015
|
+
(elm.superClass != null &&
|
|
1016
|
+
elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
|
|
1017
|
+
sc.decls[name].some((f) => isStateNode(f) &&
|
|
1018
|
+
f.type == "FunctionDeclaration" &&
|
|
1019
|
+
maybeCalled(f.node))) ||
|
|
1020
|
+
(sc.superClass && checkInherited(sc, name))));
|
|
708
1021
|
state.pre = (node) => {
|
|
709
1022
|
switch (node.type) {
|
|
710
1023
|
case "ConditionalExpression":
|
|
@@ -947,11 +1260,9 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
947
1260
|
}
|
|
948
1261
|
return null;
|
|
949
1262
|
}
|
|
950
|
-
if (callees.length == 1) {
|
|
951
|
-
const callee =
|
|
952
|
-
if (callee &&
|
|
953
|
-
callee.type == "FunctionDeclaration" &&
|
|
954
|
-
callee.optimizable &&
|
|
1263
|
+
if (callees.length == 1 && callees[0].type === "FunctionDeclaration") {
|
|
1264
|
+
const callee = callees[0].node;
|
|
1265
|
+
if (callee.optimizable &&
|
|
955
1266
|
!callee.hasOverride &&
|
|
956
1267
|
node.arguments.every((n) => getNodeValue(n)[0] !== null)) {
|
|
957
1268
|
const ret = evaluateFunction(callee, node.arguments);
|
|
@@ -960,6 +1271,13 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
960
1271
|
return null;
|
|
961
1272
|
}
|
|
962
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
|
+
}
|
|
963
1281
|
}
|
|
964
1282
|
if (!hasProperty(state.calledFunctions, name)) {
|
|
965
1283
|
state.calledFunctions[name] = [];
|
|
@@ -973,71 +1291,79 @@ async function optimizeMonkeyC(fnMap) {
|
|
|
973
1291
|
Object.values(fnMap).forEach((f) => {
|
|
974
1292
|
collectNamespaces(f.ast, state);
|
|
975
1293
|
});
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
break;
|
|
999
|
-
case "EnumDeclaration":
|
|
1000
|
-
if (!node.body.members.length) {
|
|
1001
|
-
if (!node.id)
|
|
1002
|
-
return false;
|
|
1003
|
-
if (!node.body["enumType"]) {
|
|
1004
|
-
throw new Error("Missing enumType on optimized enum");
|
|
1005
|
-
}
|
|
1006
|
-
replace(node, {
|
|
1007
|
-
type: "TypedefDeclaration",
|
|
1008
|
-
id: node.id,
|
|
1009
|
-
ts: {
|
|
1010
|
-
type: "UnaryExpression",
|
|
1011
|
-
argument: { type: "TypeSpecList", ts: [node.body.enumType] },
|
|
1012
|
-
prefix: true,
|
|
1013
|
-
operator: " as",
|
|
1014
|
-
},
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
break;
|
|
1018
|
-
case "VariableDeclaration": {
|
|
1019
|
-
node.declarations = node.declarations.filter((d) => {
|
|
1020
|
-
const name = variableDeclarationName(d.id);
|
|
1021
|
-
return (!hasProperty(state.index, name) ||
|
|
1022
|
-
hasProperty(state.exposed, name));
|
|
1023
|
-
});
|
|
1024
|
-
if (!node.declarations.length) {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
break;
|
|
1294
|
+
delete state.pre;
|
|
1295
|
+
delete state.post;
|
|
1296
|
+
const cleanup = (node) => {
|
|
1297
|
+
switch (node.type) {
|
|
1298
|
+
case "EnumStringBody":
|
|
1299
|
+
if (node.members.every((m) => {
|
|
1300
|
+
const name = "name" in m ? m.name : m.id.name;
|
|
1301
|
+
return (hasProperty(state.index, name) &&
|
|
1302
|
+
!hasProperty(state.exposed, name));
|
|
1303
|
+
})) {
|
|
1304
|
+
node.enumType = [
|
|
1305
|
+
...new Set(node.members.map((m) => {
|
|
1306
|
+
if (!("init" in m))
|
|
1307
|
+
return "Number";
|
|
1308
|
+
const [node, type] = getNodeValue(m.init);
|
|
1309
|
+
if (!node) {
|
|
1310
|
+
throw new Error("Failed to get type for eliminated enum");
|
|
1311
|
+
}
|
|
1312
|
+
return type;
|
|
1313
|
+
})),
|
|
1314
|
+
].join(" or ");
|
|
1315
|
+
node.members.splice(0);
|
|
1028
1316
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
break;
|
|
1034
|
-
case "FunctionDeclaration":
|
|
1035
|
-
if (!maybeCalled(node)) {
|
|
1317
|
+
break;
|
|
1318
|
+
case "EnumDeclaration":
|
|
1319
|
+
if (!node.body.members.length) {
|
|
1320
|
+
if (!node.id)
|
|
1036
1321
|
return false;
|
|
1322
|
+
if (!node.body["enumType"]) {
|
|
1323
|
+
throw new Error("Missing enumType on optimized enum");
|
|
1037
1324
|
}
|
|
1038
|
-
|
|
1325
|
+
replace(node, {
|
|
1326
|
+
type: "TypedefDeclaration",
|
|
1327
|
+
id: node.id,
|
|
1328
|
+
ts: {
|
|
1329
|
+
type: "UnaryExpression",
|
|
1330
|
+
argument: { type: "TypeSpecList", ts: [node.body.enumType] },
|
|
1331
|
+
prefix: true,
|
|
1332
|
+
operator: " as",
|
|
1333
|
+
},
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
break;
|
|
1337
|
+
case "VariableDeclaration": {
|
|
1338
|
+
node.declarations = node.declarations.filter((d) => {
|
|
1339
|
+
const name = variableDeclarationName(d.id);
|
|
1340
|
+
return (!hasProperty(state.index, name) || hasProperty(state.exposed, name));
|
|
1341
|
+
});
|
|
1342
|
+
if (!node.declarations.length) {
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
break;
|
|
1039
1346
|
}
|
|
1040
|
-
|
|
1347
|
+
case "ClassElement":
|
|
1348
|
+
if (!node.item) {
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
break;
|
|
1352
|
+
case "FunctionDeclaration":
|
|
1353
|
+
if (!maybeCalled(node)) {
|
|
1354
|
+
return false;
|
|
1355
|
+
}
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
return null;
|
|
1359
|
+
};
|
|
1360
|
+
Object.values(fnMap).forEach((f) => {
|
|
1361
|
+
traverseAst(f.ast, undefined, (node) => {
|
|
1362
|
+
const ret = cleanup(node);
|
|
1363
|
+
if (ret === false) {
|
|
1364
|
+
state.removeNodeComments(node, f.ast);
|
|
1365
|
+
}
|
|
1366
|
+
return ret;
|
|
1041
1367
|
});
|
|
1042
1368
|
});
|
|
1043
1369
|
}
|
|
@@ -1133,13 +1459,14 @@ async function api_getApiMapping(state) {
|
|
|
1133
1459
|
filepath: "api.mir",
|
|
1134
1460
|
}), state);
|
|
1135
1461
|
negativeFixups.forEach((fixup) => {
|
|
1136
|
-
const
|
|
1462
|
+
const vs = fixup.split(".").reduce((state, part) => {
|
|
1137
1463
|
const decls = api_isStateNode(state) && state.decls?.[part];
|
|
1138
1464
|
if (!Array.isArray(decls) || decls.length != 1 || !decls[0]) {
|
|
1139
1465
|
throw `Failed to find and fix negative constant ${fixup}`;
|
|
1140
1466
|
}
|
|
1141
1467
|
return decls[0];
|
|
1142
1468
|
}, result);
|
|
1469
|
+
const value = api_isStateNode(vs) ? vs.node : vs;
|
|
1143
1470
|
if (value.type !== "EnumStringMember" &&
|
|
1144
1471
|
(value.type !== "VariableDeclarator" || value.kind != "const")) {
|
|
1145
1472
|
throw `Negative constant ${fixup} did not refer to a constant`;
|
|
@@ -1175,6 +1502,68 @@ function api_isStateNode(node) {
|
|
|
1175
1502
|
function api_variableDeclarationName(node) {
|
|
1176
1503
|
return ("left" in node ? node.left : node).name;
|
|
1177
1504
|
}
|
|
1505
|
+
function checkOne(ns, decls, name) {
|
|
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
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
function lookup(state, decls, node, name, stack) {
|
|
1523
|
+
stack || (stack = state.stack);
|
|
1524
|
+
switch (node.type) {
|
|
1525
|
+
case "MemberExpression": {
|
|
1526
|
+
if (node.property.type != "Identifier" || node.computed)
|
|
1527
|
+
break;
|
|
1528
|
+
const [, module, where] = lookup(state, decls, node.object, name, stack);
|
|
1529
|
+
if (module && module.length === 1) {
|
|
1530
|
+
const result = checkOne(module[0], decls, node.property.name);
|
|
1531
|
+
if (result) {
|
|
1532
|
+
return [
|
|
1533
|
+
name || node.property.name,
|
|
1534
|
+
result,
|
|
1535
|
+
where.concat(module[0]),
|
|
1536
|
+
];
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
case "ThisExpression": {
|
|
1542
|
+
for (let i = stack.length; i--;) {
|
|
1543
|
+
const si = stack[i];
|
|
1544
|
+
if (si.type == "ModuleDeclaration" ||
|
|
1545
|
+
si.type == "ClassDeclaration" ||
|
|
1546
|
+
!i) {
|
|
1547
|
+
return [name || si.name, [si], stack.slice(0, i)];
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
case "Identifier": {
|
|
1553
|
+
if (node.name == "$") {
|
|
1554
|
+
return [name || node.name, [stack[0]], []];
|
|
1555
|
+
}
|
|
1556
|
+
for (let i = stack.length; i--;) {
|
|
1557
|
+
const result = checkOne(stack[i], decls, node.name);
|
|
1558
|
+
if (result) {
|
|
1559
|
+
return [name || node.name, result, stack.slice(0, i + 1)];
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
break;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return [null, null, null];
|
|
1566
|
+
}
|
|
1178
1567
|
function api_collectNamespaces(ast, stateIn) {
|
|
1179
1568
|
const state = (stateIn || {});
|
|
1180
1569
|
if (!state.index)
|
|
@@ -1184,57 +1573,30 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1184
1573
|
{ type: "Program", name: "$", fullName: "$", node: undefined },
|
|
1185
1574
|
];
|
|
1186
1575
|
}
|
|
1187
|
-
|
|
1188
|
-
if (
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
stack || (stack = state.stack);
|
|
1195
|
-
switch (node.type) {
|
|
1196
|
-
case "MemberExpression": {
|
|
1197
|
-
if (node.property.type != "Identifier" || node.computed)
|
|
1198
|
-
break;
|
|
1199
|
-
const [, module, where] = state.lookup(node.object, name, stack);
|
|
1200
|
-
if (module && module.length === 1) {
|
|
1201
|
-
const result = checkOne(module[0], node.property.name);
|
|
1202
|
-
if (result) {
|
|
1203
|
-
return [
|
|
1204
|
-
name || node.property.name,
|
|
1205
|
-
result,
|
|
1206
|
-
where.concat(module[0]),
|
|
1207
|
-
];
|
|
1208
|
-
}
|
|
1576
|
+
state.removeNodeComments = (node, ast) => {
|
|
1577
|
+
if (node.start && node.end && ast.comments && ast.comments.length) {
|
|
1578
|
+
let low = 0, high = ast.comments.length;
|
|
1579
|
+
while (high > low) {
|
|
1580
|
+
const mid = (low + high) >> 1;
|
|
1581
|
+
if (ast.comments[mid].start < node.start) {
|
|
1582
|
+
low = mid + 1;
|
|
1209
1583
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
case "ThisExpression": {
|
|
1213
|
-
for (let i = stack.length; i--;) {
|
|
1214
|
-
const si = stack[i];
|
|
1215
|
-
if (si.type == "ModuleDeclaration" ||
|
|
1216
|
-
si.type == "ClassDeclaration" ||
|
|
1217
|
-
!i) {
|
|
1218
|
-
return [name || si.name, [si], stack.slice(0, i)];
|
|
1219
|
-
}
|
|
1584
|
+
else {
|
|
1585
|
+
high = mid;
|
|
1220
1586
|
}
|
|
1221
|
-
break;
|
|
1222
1587
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const result = checkOne(stack[i], node.name);
|
|
1229
|
-
if (result) {
|
|
1230
|
-
return [name || node.name, result, stack.slice(0, i + 1)];
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
break;
|
|
1588
|
+
while (high < ast.comments.length && ast.comments[high].end < node.end) {
|
|
1589
|
+
high++;
|
|
1590
|
+
}
|
|
1591
|
+
if (high > low) {
|
|
1592
|
+
ast.comments.splice(low, high - low);
|
|
1234
1593
|
}
|
|
1235
1594
|
}
|
|
1236
|
-
return [null, null, null];
|
|
1237
1595
|
};
|
|
1596
|
+
state.lookup = (node, name, stack) => lookup(state, state.inType ? "type_decls" : "decls", node, name, stack);
|
|
1597
|
+
state.lookupValue = (node, name, stack) => lookup(state, "decls", node, name, stack);
|
|
1598
|
+
state.lookupType = (node, name, stack) => lookup(state, "type_decls", node, name, stack);
|
|
1599
|
+
state.inType = false;
|
|
1238
1600
|
state.traverse = (root) => api_traverseAst(root, (node) => {
|
|
1239
1601
|
try {
|
|
1240
1602
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
@@ -1247,6 +1609,9 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1247
1609
|
throw new Error("Unexpected stack length for Program node");
|
|
1248
1610
|
}
|
|
1249
1611
|
break;
|
|
1612
|
+
case "TypeSpecList":
|
|
1613
|
+
state.inType = true;
|
|
1614
|
+
break;
|
|
1250
1615
|
case "BlockStatement": {
|
|
1251
1616
|
const [parent] = state.stack.slice(-1);
|
|
1252
1617
|
if (parent.type != "FunctionDeclaration" &&
|
|
@@ -1259,7 +1624,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1259
1624
|
case "FunctionDeclaration":
|
|
1260
1625
|
case "ModuleDeclaration": {
|
|
1261
1626
|
const [parent] = state.stack.slice(-1);
|
|
1262
|
-
const name = "id" in node ? node.id && node.id.name :
|
|
1627
|
+
const name = "id" in node ? node.id && node.id.name : undefined;
|
|
1263
1628
|
const fullName = state.stack
|
|
1264
1629
|
.map((e) => e.name)
|
|
1265
1630
|
.concat(name)
|
|
@@ -1294,6 +1659,15 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1294
1659
|
node.params.forEach((p) => (decls[api_variableDeclarationName(p)] = [p]));
|
|
1295
1660
|
}
|
|
1296
1661
|
parent.decls[name].push(elm);
|
|
1662
|
+
if (node.type == "ModuleDeclaration" ||
|
|
1663
|
+
node.type == "ClassDeclaration") {
|
|
1664
|
+
if (!parent.type_decls)
|
|
1665
|
+
parent.type_decls = {};
|
|
1666
|
+
if (!api_hasProperty(parent.type_decls, name)) {
|
|
1667
|
+
parent.type_decls[name] = [];
|
|
1668
|
+
}
|
|
1669
|
+
parent.type_decls[name].push(elm);
|
|
1670
|
+
}
|
|
1297
1671
|
}
|
|
1298
1672
|
break;
|
|
1299
1673
|
}
|
|
@@ -1308,14 +1682,25 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1308
1682
|
}
|
|
1309
1683
|
// fall through
|
|
1310
1684
|
case "TypedefDeclaration": {
|
|
1685
|
+
state.inType = true;
|
|
1311
1686
|
const name = node.id.name;
|
|
1312
1687
|
const [parent] = state.stack.slice(-1);
|
|
1313
|
-
if (!parent.
|
|
1314
|
-
parent.
|
|
1315
|
-
if (!api_hasProperty(parent.
|
|
1316
|
-
parent.
|
|
1688
|
+
if (!parent.type_decls)
|
|
1689
|
+
parent.type_decls = {};
|
|
1690
|
+
if (!api_hasProperty(parent.type_decls, name)) {
|
|
1691
|
+
parent.type_decls[name] = [];
|
|
1317
1692
|
}
|
|
1318
|
-
(
|
|
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
|
+
});
|
|
1319
1704
|
break;
|
|
1320
1705
|
}
|
|
1321
1706
|
case "VariableDeclaration": {
|
|
@@ -1323,13 +1708,23 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1323
1708
|
if (!parent.decls)
|
|
1324
1709
|
parent.decls = {};
|
|
1325
1710
|
const decls = parent.decls;
|
|
1711
|
+
const stack = state.stack.slice();
|
|
1326
1712
|
node.declarations.forEach((decl) => {
|
|
1327
1713
|
const name = api_variableDeclarationName(decl.id);
|
|
1328
1714
|
if (!api_hasProperty(decls, name)) {
|
|
1329
1715
|
decls[name] = [];
|
|
1330
1716
|
}
|
|
1717
|
+
else if (decls[name].find((n) => (api_isStateNode(n) ? n.node : n) == decl)) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1331
1720
|
decl.kind = node.kind;
|
|
1332
|
-
(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
|
+
});
|
|
1333
1728
|
if (node.kind == "const") {
|
|
1334
1729
|
if (!api_hasProperty(state.index, name)) {
|
|
1335
1730
|
state.index[name] = [];
|
|
@@ -1340,6 +1735,7 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1340
1735
|
break;
|
|
1341
1736
|
}
|
|
1342
1737
|
case "EnumStringBody": {
|
|
1738
|
+
state.inType = false;
|
|
1343
1739
|
const [parent] = state.stack.slice(-1);
|
|
1344
1740
|
const values = parent.decls || (parent.decls = {});
|
|
1345
1741
|
let prev = -1;
|
|
@@ -1400,12 +1796,28 @@ function api_collectNamespaces(ast, stateIn) {
|
|
|
1400
1796
|
let ret;
|
|
1401
1797
|
if (state.shouldExclude && state.shouldExclude(node)) {
|
|
1402
1798
|
// delete the node.
|
|
1403
|
-
|
|
1799
|
+
ret = false;
|
|
1404
1800
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1801
|
+
else {
|
|
1802
|
+
const type = node.type;
|
|
1803
|
+
if (state.post)
|
|
1804
|
+
ret = state.post(node, state);
|
|
1805
|
+
switch (type) {
|
|
1806
|
+
case "TypeSpecList":
|
|
1807
|
+
case "TypedefDeclaration":
|
|
1808
|
+
case "EnumDeclaration":
|
|
1809
|
+
state.inType = false;
|
|
1810
|
+
break;
|
|
1811
|
+
case "EnumStringBody":
|
|
1812
|
+
state.inType = true;
|
|
1813
|
+
break;
|
|
1814
|
+
}
|
|
1815
|
+
if (state.stack.slice(-1).pop()?.node === node) {
|
|
1816
|
+
state.stack.pop();
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
if (ret === false) {
|
|
1820
|
+
state.removeNodeComments(node, ast);
|
|
1409
1821
|
}
|
|
1410
1822
|
return ret;
|
|
1411
1823
|
}
|
|
@@ -1500,22 +1912,28 @@ function formatAst(node, monkeyCSource = null) {
|
|
|
1500
1912
|
});
|
|
1501
1913
|
}
|
|
1502
1914
|
function handleException(state, node, exception) {
|
|
1503
|
-
let message;
|
|
1504
1915
|
try {
|
|
1505
1916
|
const fullName = state.stack
|
|
1506
1917
|
.map((e) => e.name)
|
|
1507
|
-
.concat("name" in node && typeof node.name === "string" ? node.name :
|
|
1918
|
+
.concat("name" in node && typeof node.name === "string" ? node.name : undefined)
|
|
1508
1919
|
.filter((e) => e != null)
|
|
1509
1920
|
.join(".");
|
|
1510
1921
|
const location = node.loc && node.loc.source
|
|
1511
1922
|
? `${node.loc.source}:${node.start || 0}:${node.end || 0}`
|
|
1512
1923
|
: "<unknown>";
|
|
1513
|
-
message = `Got exception \`${
|
|
1924
|
+
const message = `Got exception \`${exception instanceof Error
|
|
1925
|
+
? exception.message
|
|
1926
|
+
: Object.prototype.toString.call(exception)}' while processing node ${fullName}:${node.type} from ${location}`;
|
|
1927
|
+
if (exception instanceof Error) {
|
|
1928
|
+
exception.message = message;
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
exception = new Error(message);
|
|
1932
|
+
}
|
|
1514
1933
|
}
|
|
1515
|
-
|
|
1934
|
+
finally {
|
|
1516
1935
|
throw exception;
|
|
1517
1936
|
}
|
|
1518
|
-
throw new Error(message);
|
|
1519
1937
|
}
|
|
1520
1938
|
|
|
1521
1939
|
})();
|