@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/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(parent.decls, name))
252
- parent.decls[name] = [];
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
- return node.attrs.attrs.reduce((drop, attr) => {
336
- if (attr.type != "UnaryExpression")
337
- return drop;
338
- if (attr.argument.type != "Identifier")
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
- if (hasProperty(excludeAnnotations, attr.argument.name)) {
341
- return true;
342
- }
343
- if (attr.argument.name == "test") {
344
- hasTests = true;
345
- }
346
- return drop;
347
- }, false);
659
+ }, false);
660
+ }
348
661
  }
349
- return null;
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.some((sc) => (hasProperty(sc.decls, name) &&
704
- sc.decls[name].some((f) => isStateNode(f) &&
705
- f.type == "FunctionDeclaration" &&
706
- maybeCalled(f.node))) ||
707
- (sc.superClass && checkInherited(sc, name)));
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 = isStateNode(callees[0]) && callees[0].node;
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
- Object.values(fnMap).forEach((f) => {
977
- traverseAst(f.ast, undefined, (node) => {
978
- switch (node.type) {
979
- case "EnumStringBody":
980
- if (node.members.every((m) => {
981
- const name = "name" in m ? m.name : m.id.name;
982
- return (hasProperty(state.index, name) &&
983
- !hasProperty(state.exposed, name));
984
- })) {
985
- node.enumType = [
986
- ...new Set(node.members.map((m) => {
987
- if (!("init" in m))
988
- return "Number";
989
- const [node, type] = getNodeValue(m.init);
990
- if (!node) {
991
- throw new Error("Failed to get type for eliminated enum");
992
- }
993
- return type;
994
- })),
995
- ].join(" or ");
996
- node.members.splice(0);
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
- case "ClassElement":
1030
- if (!node.item) {
1031
- return false;
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
- break;
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
- return null;
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 value = fixup.split(".").reduce((state, part) => {
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
- const checkOne = (ns, name) => {
1188
- if (api_isStateNode(ns) && api_hasProperty(ns.decls, name)) {
1189
- return ns.decls[name];
1190
- }
1191
- return null;
1192
- };
1193
- state.lookup = (node, name, stack) => {
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
- break;
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
- case "Identifier": {
1224
- if (node.name == "$") {
1225
- return [name || node.name, [stack[0]], []];
1226
- }
1227
- for (let i = stack.length; i--;) {
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 : null;
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.decls)
1314
- parent.decls = {};
1315
- if (!api_hasProperty(parent.decls, name)) {
1316
- parent.decls[name] = [];
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
- (0,external_util_cjs_namespaceObject.pushUnique)(parent.decls[name], node);
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], decl);
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
- return false;
1799
+ ret = false;
1404
1800
  }
1405
- if (state.post)
1406
- ret = state.post(node, state);
1407
- if (state.stack.slice(-1).pop()?.node === node) {
1408
- state.stack.pop();
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 : null)
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 \`${Object.prototype.toString.call(exception)}' while processing node ${fullName}:${node.type} from ${location}`;
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
- catch {
1934
+ finally {
1516
1935
  throw exception;
1517
1936
  }
1518
- throw new Error(message);
1519
1937
  }
1520
1938
 
1521
1939
  })();