@markw65/monkeyc-optimizer 1.0.3 → 1.0.7

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
@@ -1,3 +1,4 @@
1
+ 0 && (module.exports = {LiteralIntegerRe,getApiMapping,hasProperty,collectNamespaces,traverseAst,formatAst});
1
2
  /******/ (() => { // webpackBootstrap
2
3
  /******/ "use strict";
3
4
  /******/ // The require scope
@@ -39,12 +40,12 @@ __webpack_require__.r(__webpack_exports__);
39
40
 
40
41
  // EXPORTS
41
42
  __webpack_require__.d(__webpack_exports__, {
42
- "LiteralIntegerRe": () => (/* binding */ LiteralIntegerRe),
43
- "collectNamespaces": () => (/* binding */ collectNamespaces),
43
+ "LiteralIntegerRe": () => (/* binding */ api_LiteralIntegerRe),
44
+ "collectNamespaces": () => (/* binding */ api_collectNamespaces),
44
45
  "formatAst": () => (/* binding */ formatAst),
45
- "getApiMapping": () => (/* binding */ getApiMapping),
46
- "hasProperty": () => (/* binding */ hasProperty),
47
- "traverseAst": () => (/* binding */ traverseAst)
46
+ "getApiMapping": () => (/* binding */ api_getApiMapping),
47
+ "hasProperty": () => (/* binding */ api_hasProperty),
48
+ "traverseAst": () => (/* binding */ api_traverseAst)
48
49
  });
49
50
 
50
51
  ;// CONCATENATED MODULE: external "@markw65/prettier-plugin-monkeyc"
@@ -111,6 +112,663 @@ const negativeFixups = [
111
112
  "Toybox.WatchUi.LAYOUT_HALIGN_LEFT",
112
113
  ];
113
114
 
115
+ ;// CONCATENATED MODULE: external "./api.cjs"
116
+ const external_api_cjs_namespaceObject = require("./api.cjs");
117
+ ;// CONCATENATED MODULE: ./src/mc-rewrite.js
118
+
119
+
120
+
121
+
122
+
123
+ function processImports(allImports, lookup) {
124
+ allImports.forEach(({ node, stack }) => {
125
+ const [name, module] = lookup(node.id, node.as && node.as.name, stack);
126
+ if (name && module) {
127
+ const [parent] = stack.slice(-1);
128
+ if (!parent.decls) parent.decls = {};
129
+ if (!hasProperty(parent.decls, name)) parent.decls[name] = [];
130
+ module.forEach((m) => {
131
+ if (m.type == "ModuleDeclaration") {
132
+ pushUnique(parent.decls[name], m);
133
+ }
134
+ });
135
+ }
136
+ });
137
+ }
138
+
139
+ function collectClassInfo(state) {
140
+ state.allClasses.forEach((elm) => {
141
+ if (elm.node.superClass) {
142
+ const [, classes] = state.lookup(elm.node.superClass, null, elm.stack);
143
+ if (classes) {
144
+ elm.superClass = classes.filter((c) => c.type == "ClassDeclaration");
145
+ }
146
+ // set it "true" if there is a superClass, but we can't find it.
147
+ if (!elm.superClass || !elm.superClass.length) elm.superClass = true;
148
+ }
149
+ });
150
+
151
+ const markOverrides = (cls, scls) => {
152
+ if (scls === true) return;
153
+ scls.forEach((c) => {
154
+ c.decls &&
155
+ Object.values(c.decls).forEach((f) => {
156
+ if (f.type == "FunctionDeclaration") {
157
+ if (hasProperty(cls.decls, f.name)) {
158
+ f.hasOverride = true;
159
+ }
160
+ }
161
+ });
162
+ if (c.superClass) markOverrides(cls, c.superClass);
163
+ });
164
+ };
165
+
166
+ state.allClasses.forEach((elm) => {
167
+ if (elm.superClass) markOverrides(elm, elm.superClass);
168
+ });
169
+ }
170
+
171
+ async function analyze(fileNames, buildConfig) {
172
+ const excludeAnnotations =
173
+ buildConfig && buildConfig.excludeAnnotations
174
+ ? Object.fromEntries(buildConfig.excludeAnnotations.map((a) => [a, true]))
175
+ : {};
176
+
177
+ const allImports = [];
178
+ const state = {
179
+ allFunctions: [],
180
+ allClasses: [],
181
+ shouldExclude(node) {
182
+ if (node.attrs && node.attrs.attrs) {
183
+ if (
184
+ node.attrs.attrs.filter((attr) => {
185
+ if (attr.type != "UnaryExpression") return false;
186
+ if (attr.argument.type != "Identifier") return false;
187
+ return hasProperty(excludeAnnotations, attr.argument.name);
188
+ }).length
189
+ ) {
190
+ return true;
191
+ }
192
+ }
193
+ },
194
+ post(node) {
195
+ switch (node.type) {
196
+ case "FunctionDeclaration":
197
+ case "ClassDeclaration": {
198
+ const [scope] = state.stack.slice(-1);
199
+ const stack = state.stack.slice(0, -1);
200
+ scope.stack = stack;
201
+ (node.type == "FunctionDeclaration"
202
+ ? state.allFunctions
203
+ : state.allClasses
204
+ ).push(scope);
205
+ return;
206
+ }
207
+ case "Using":
208
+ case "ImportModule":
209
+ allImports.push({ node, stack: state.stack.slice() });
210
+ return;
211
+ }
212
+ },
213
+ };
214
+
215
+ await getApiMapping(state);
216
+
217
+ // Mark all functions from api.mir as "special" by
218
+ // setting their bodies to null. In api.mir, they're
219
+ // all empty, which makes it look like they're
220
+ // do-nothing functions.
221
+ const markApi = (node) => {
222
+ if (node.type == "FunctionDeclaration") {
223
+ node.node.body = null;
224
+ }
225
+ if (node.decls) {
226
+ Object.values(node.decls).forEach(markApi);
227
+ }
228
+ };
229
+ markApi(state.stack[0]);
230
+
231
+ const files = await Promise.all(
232
+ fileNames.map(async (name) => ({
233
+ name,
234
+ monkeyCSource: (await fs.readFile(name))
235
+ .toString()
236
+ .replace(/\r\n/g, "\n"),
237
+ }))
238
+ );
239
+
240
+ files.forEach((f) => {
241
+ f.ast = MonkeyC.parsers.monkeyc.parse(f.monkeyCSource, {
242
+ grammarSource: f.name,
243
+ });
244
+ f.ast.source = f.name;
245
+ f.ast.monkeyCSource = f.monkeyCSource;
246
+ delete f.monkeyCSource;
247
+ collectNamespaces(f.ast, state);
248
+ });
249
+
250
+ delete state.shouldExclude;
251
+ delete state.post;
252
+
253
+ processImports(allImports, state.lookup);
254
+ collectClassInfo(state);
255
+
256
+ return { files, state };
257
+ }
258
+
259
+ function getLiteralNode(node) {
260
+ if (Array.isArray(node)) {
261
+ if (!node.length) return null;
262
+ if (node.length === 1) return getLiteralNode(node[0]);
263
+ let result;
264
+ if (
265
+ node.every((n) => {
266
+ const lit = getLiteralNode(n);
267
+ if (!lit) return false;
268
+ if (!result) {
269
+ result = lit;
270
+ } else {
271
+ if (lit.value !== result.value) return false;
272
+ }
273
+ return true;
274
+ })
275
+ ) {
276
+ return result;
277
+ }
278
+ return null;
279
+ }
280
+ if (node.type == "Literal") return node;
281
+ if (node.type == "BinaryExpression" && node.operator == "as") {
282
+ return getLiteralNode(node.left) && node;
283
+ }
284
+ if (node.type == "UnaryExpression") {
285
+ if (node.argument.type != "Literal") return null;
286
+ switch (node.operator) {
287
+ case "-":
288
+ if (typeof node.argument.value == "number") {
289
+ return {
290
+ ...node.argument,
291
+ value: -node.argument.value,
292
+ raw: "-" + node.argument.value,
293
+ enumType: node.enumType,
294
+ };
295
+ }
296
+ }
297
+ }
298
+ }
299
+
300
+ function getNodeValue(node) {
301
+ if (
302
+ node.type == "BinaryExpression" &&
303
+ node.operator == "as" &&
304
+ node.right.type == "TypeSpecList" &&
305
+ node.right.ts.length == 1 &&
306
+ typeof node.right.ts[0] == "string"
307
+ ) {
308
+ // this is a cast we inserted to retain the type of an enum
309
+ // any arithmetic on it will revert to "Number", or "Long",
310
+ // so just ignore it.
311
+ return getNodeValue(node.left);
312
+ }
313
+ if (node.type != "Literal") {
314
+ return [null, null];
315
+ }
316
+ let type = node.value === null ? "Null" : typeof node.value;
317
+ if (type === "number") {
318
+ const match = LiteralIntegerRe.exec(node.raw);
319
+ if (match) {
320
+ type = match[2] == "l" ? "Long" : "Number";
321
+ } else if (node.raw.endsWith("d")) {
322
+ type = "Double";
323
+ } else {
324
+ type = "Float";
325
+ }
326
+ } else if (type === "string") {
327
+ type = "String";
328
+ } else if (type === "boolean") {
329
+ type = "Boolean";
330
+ } else {
331
+ type = "Unknown";
332
+ }
333
+ return [node, type];
334
+ }
335
+
336
+ function optimizeNode(node) {
337
+ switch (node.type) {
338
+ case "UnaryExpression": {
339
+ const [arg, type] = getNodeValue(node.argument);
340
+ if (arg === null) break;
341
+ switch (node.operator) {
342
+ case "+":
343
+ if (type === "Number" || type === "Long") {
344
+ return arg;
345
+ }
346
+ break;
347
+ case "-":
348
+ if (type === "Number" || type === "Long") {
349
+ return {
350
+ ...arg,
351
+ value: -arg.value,
352
+ raw: (-arg.value).toString() + (type === "Long" ? "l" : ""),
353
+ };
354
+ }
355
+ break;
356
+ case "!":
357
+ case "~":
358
+ {
359
+ let value;
360
+ if (type === "Number" || type === "Long") {
361
+ value = -arg.value - 1;
362
+ } else if (type === "Boolean" && node.operator == "!") {
363
+ value = !arg.value;
364
+ }
365
+ if (value !== undefined) {
366
+ return {
367
+ ...arg,
368
+ value,
369
+ raw: value.toString() + (type === "Long" ? "l" : ""),
370
+ };
371
+ }
372
+ }
373
+ break;
374
+ }
375
+ break;
376
+ }
377
+ case "BinaryExpression": {
378
+ const operators = {
379
+ "+": (left, right) => left + right,
380
+ "-": (left, right) => left - right,
381
+ "*": (left, right) => left * right,
382
+ "/": (left, right) => Math.trunc(left / right),
383
+ "%": (left, right) => left % right,
384
+ "&": (left, right, type) => (type === "Number" ? left & right : null),
385
+ "|": (left, right, type) => (type === "Number" ? left | right : null),
386
+ "<<": (left, right, type) => (type === "Number" ? left << right : null),
387
+ ">>": (left, right, type) => (type === "Number" ? left >> right : null),
388
+ };
389
+ const op = operators[node.operator];
390
+ if (op) {
391
+ const [left, left_type] = getNodeValue(node.left);
392
+ const [right, right_type] = getNodeValue(node.right);
393
+ if (!left || !right) break;
394
+ if (
395
+ left_type != right_type ||
396
+ (left_type != "Number" && left_type != "Long")
397
+ ) {
398
+ break;
399
+ }
400
+ const value = op(left.value, right.value, left_type);
401
+ if (value === null) break;
402
+ return {
403
+ ...left,
404
+ value,
405
+ raw: value.toString() + (left_type === "Long" ? "l" : ""),
406
+ };
407
+ }
408
+ break;
409
+ }
410
+ case "FunctionDeclaration":
411
+ if (node.body && evaluateFunction(node, null) !== false) {
412
+ node.optimizable = true;
413
+ }
414
+ break;
415
+ }
416
+ }
417
+
418
+ function evaluateFunction(func, args) {
419
+ if (args && args.length != func.params.length) {
420
+ return false;
421
+ }
422
+ const paramValues =
423
+ args && Object.fromEntries(func.params.map((p, i) => [p.name, args[i]]));
424
+ let ret = null;
425
+ const body = args ? JSON.parse(JSON.stringify(func.body)) : func.body;
426
+ try {
427
+ traverseAst(
428
+ body,
429
+ (node) => {
430
+ switch (node.type) {
431
+ case "BlockStatement":
432
+ case "ReturnStatement":
433
+ case "UnaryExpression":
434
+ case "BinaryExpression":
435
+ case "Literal":
436
+ case "Identifier":
437
+ return;
438
+ default:
439
+ throw new Error("Bad node type");
440
+ }
441
+ },
442
+ args &&
443
+ ((node) => {
444
+ switch (node.type) {
445
+ case "ReturnStatement":
446
+ ret = node.argument;
447
+ return;
448
+ case "BlockStatement":
449
+ case "Literal":
450
+ return;
451
+ case "Identifier":
452
+ if (hasProperty(paramValues, node.name)) {
453
+ return paramValues[node.name];
454
+ }
455
+ // fall through;
456
+ default: {
457
+ const repl = optimizeNode(node);
458
+ if (repl && repl.type === "Literal") return repl;
459
+ throw new Error("Didn't optimize");
460
+ }
461
+ }
462
+ })
463
+ );
464
+ return ret;
465
+ } catch (e) {
466
+ return false;
467
+ }
468
+ }
469
+
470
+ async function optimizeMonkeyC(fileNames, buildConfig) {
471
+ const { files, state } = await analyze(fileNames, buildConfig);
472
+ const replace = (node, obj) => {
473
+ for (const k of Object.keys(node)) {
474
+ delete node[k];
475
+ }
476
+ if (obj.enumType) {
477
+ obj = {
478
+ type: "BinaryExpression",
479
+ operator: "as",
480
+ left: obj,
481
+ right: { type: "TypeSpecList", ts: [obj.enumType] },
482
+ };
483
+ }
484
+ for (const [k, v] of Object.entries(obj)) {
485
+ node[k] = v;
486
+ }
487
+ };
488
+ const lookupAndReplace = (node) => {
489
+ const [, objects] = state.lookup(node);
490
+ if (!objects) {
491
+ return false;
492
+ }
493
+ const obj = getLiteralNode(objects);
494
+ if (!obj) {
495
+ return false;
496
+ }
497
+ replace(node, obj);
498
+ return true;
499
+ };
500
+
501
+ /*
502
+ * Might this function be called from somewhere, including
503
+ * callbacks from the api (eg getSettingsView, etc).
504
+ */
505
+ const maybeCalled = (func) => {
506
+ if (!func.body) {
507
+ // this is an api.mir function. It can be called
508
+ return true;
509
+ }
510
+ if (hasProperty(state.exposed, func.id.name)) return true;
511
+ if (hasProperty(state.calledFunctions, func.id.name)) {
512
+ return (
513
+ state.calledFunctions[func.id.name].find((f) => f === func) !== null
514
+ );
515
+ }
516
+ };
517
+ /*
518
+ * Does elm (a class) have a maybeCalled function called name,
519
+ * anywhere in its superClass chain.
520
+ */
521
+ const checkInherited = (elm, name) =>
522
+ elm.superClass === true ||
523
+ elm.superClass.some(
524
+ (sc) =>
525
+ (hasProperty(sc.decls, name) &&
526
+ sc.decls[name].some(
527
+ (f) => f.type == "FunctionDeclaration" && maybeCalled(f)
528
+ )) ||
529
+ (sc.superClass && checkInherited(sc, name))
530
+ );
531
+
532
+ state.exposed = {};
533
+ state.calledFunctions = {};
534
+ state.pre = (node) => {
535
+ switch (node.type) {
536
+ case "ConditionalExpression":
537
+ case "IfStatement":
538
+ case "DoWhileStatement":
539
+ case "WhileStatement":
540
+ state.traverse(node.test);
541
+ const [value, type] = getNodeValue(node.test);
542
+ if (value) {
543
+ let result = null;
544
+ if (type === "Null") {
545
+ result = false;
546
+ } else if (
547
+ type === "Boolean" ||
548
+ type === "Number" ||
549
+ type === "Long"
550
+ ) {
551
+ result = !!value.value;
552
+ }
553
+ if (result !== null) {
554
+ if (
555
+ node.type === "IfStatement" ||
556
+ node.type === "ConditionalExpression"
557
+ ) {
558
+ if (result === false) {
559
+ node.consequent = null;
560
+ } else {
561
+ node.alternate = null;
562
+ }
563
+ node.test = result;
564
+ } else if (node.type === "WhileStatement") {
565
+ if (result === false) {
566
+ node.body = null;
567
+ }
568
+ } else if (node.type === "DoWhileStatement") {
569
+ if (result === false) {
570
+ node.test = null;
571
+ }
572
+ } else {
573
+ throw new Error("Unexpected Node type");
574
+ }
575
+ }
576
+ }
577
+ return;
578
+
579
+ case "EnumDeclaration":
580
+ return false;
581
+ case "VariableDeclarator":
582
+ return ["init"];
583
+ case "UnaryExpression":
584
+ if (node.operator == ":") {
585
+ // If we produce a Symbol, for a given name,
586
+ // its possible that someone uses that symbol
587
+ // indirectly, so we can't remove any enums or
588
+ // constants with that name (we can still replace
589
+ // uses of those constants though).
590
+ state.exposed[node.argument.name] = true;
591
+ // In any case, we can't replace *this* use of the
592
+ // symbol with its value...
593
+ return false;
594
+ }
595
+ break;
596
+ case "Identifier": {
597
+ if (hasProperty(state.index, node.name)) {
598
+ if (!lookupAndReplace(node)) {
599
+ state.exposed[node.name] = true;
600
+ }
601
+ }
602
+ return false;
603
+ }
604
+ case "MemberExpression":
605
+ if (node.property.type === "Identifier" && !node.computed) {
606
+ if (hasProperty(state.index, node.property.name)) {
607
+ if (lookupAndReplace(node)) {
608
+ return false;
609
+ } else {
610
+ state.exposed[node.property.name] = true;
611
+ }
612
+ }
613
+ // Don't optimize the property.
614
+ return ["object"];
615
+ }
616
+ break;
617
+ case "FunctionDeclaration": {
618
+ const [parent] = state.stack.slice(-2);
619
+ if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
620
+ let used = false;
621
+ if (node.id.name == "initialize") {
622
+ used = true;
623
+ } else if (parent.superClass) {
624
+ used = checkInherited(parent, node.id.name);
625
+ }
626
+ if (used) {
627
+ if (!hasProperty(state.calledFunctions, node.id.name)) {
628
+ state.calledFunctions[node.id.name] = [];
629
+ }
630
+ state.calledFunctions[node.id.name].push(node);
631
+ }
632
+ }
633
+ }
634
+ }
635
+ };
636
+ state.post = (node) => {
637
+ const opt = optimizeNode(node);
638
+ if (opt) {
639
+ replace(node, opt);
640
+ return;
641
+ }
642
+ switch (node.type) {
643
+ case "ConditionalExpression":
644
+ case "IfStatement":
645
+ if (typeof node.test === "boolean") {
646
+ const rep = node.test ? node.consequent : node.alternate;
647
+ if (!rep) return false;
648
+ replace(node, rep);
649
+ }
650
+ break;
651
+ case "WhileStatement":
652
+ if (!node.body) return false;
653
+ break;
654
+ case "DoWhileStatement":
655
+ if (!node.test) return node.body;
656
+ break;
657
+
658
+ case "CallExpression": {
659
+ const [name, callees] = state.lookup(node.callee);
660
+ if (!callees || !callees.length) {
661
+ const n =
662
+ name ||
663
+ node.callee.name ||
664
+ (node.callee.property && node.callee.property.name);
665
+ if (n) {
666
+ state.exposed[n] = true;
667
+ } else {
668
+ // There are unnamed CallExpressions, such as new [size]
669
+ // So there's nothing to do here.
670
+ }
671
+ return;
672
+ }
673
+ if (callees.length == 1) {
674
+ const callee = callees[0].node;
675
+ if (
676
+ callee.optimizable &&
677
+ !callee.hasOverride &&
678
+ node.arguments.every((n) => getNodeValue(n)[0] !== null)
679
+ ) {
680
+ const ret = evaluateFunction(callee, node.arguments);
681
+ if (ret) {
682
+ replace(node, ret);
683
+ return;
684
+ }
685
+ }
686
+ }
687
+ if (!hasProperty(state.calledFunctions, name)) {
688
+ state.calledFunctions[name] = [];
689
+ }
690
+ callees.forEach((c) => state.calledFunctions[name].push(c.node));
691
+ break;
692
+ }
693
+ }
694
+ };
695
+ files.forEach((f) => {
696
+ collectNamespaces(f.ast, state);
697
+ });
698
+ files.forEach((f) => {
699
+ traverseAst(f.ast, null, (node) => {
700
+ switch (node.type) {
701
+ case "EnumStringBody":
702
+ if (
703
+ node.members.every((m) => {
704
+ const name = m.name || m.id.name;
705
+ return (
706
+ hasProperty(state.index, name) &&
707
+ !hasProperty(state.exposed, name)
708
+ );
709
+ })
710
+ ) {
711
+ node.enumType = [
712
+ ...new Set(
713
+ node.members.map((m) => {
714
+ if (!m.init) return "Number";
715
+ const [node, type] = getNodeValue(m.init);
716
+ if (!node) {
717
+ throw new Error("Failed to get type for eliminated enum");
718
+ }
719
+ return type;
720
+ })
721
+ ),
722
+ ].join(" or ");
723
+ node.members.splice(0);
724
+ }
725
+ break;
726
+ case "EnumDeclaration":
727
+ if (!node.body.members.length) {
728
+ if (!node.id) return false;
729
+ if (!node.body.enumType) {
730
+ throw new Error("Missing enumType on optimized enum");
731
+ }
732
+ replace(node, {
733
+ type: "TypedefDeclaration",
734
+ id: node.id,
735
+ ts: {
736
+ type: "UnaryExpression",
737
+ argument: { type: "TypeSpecList", ts: [node.body.enumType] },
738
+ prefix: true,
739
+ operator: " as",
740
+ },
741
+ });
742
+ }
743
+ break;
744
+ case "VariableDeclaration": {
745
+ node.declarations = node.declarations.filter(
746
+ (d) =>
747
+ !hasProperty(state.index, d.id.name) ||
748
+ hasProperty(state.exposed, d.id.name)
749
+ );
750
+ if (!node.declarations.length) {
751
+ return false;
752
+ }
753
+ break;
754
+ }
755
+ case "ClassElement":
756
+ if (!node.item) {
757
+ return false;
758
+ }
759
+ break;
760
+ case "FunctionDeclaration":
761
+ if (!maybeCalled(node)) {
762
+ return false;
763
+ }
764
+ break;
765
+ }
766
+ });
767
+ });
768
+
769
+ return files;
770
+ }
771
+
114
772
  ;// CONCATENATED MODULE: ./src/api.js
115
773
 
116
774
 
@@ -118,7 +776,8 @@ const negativeFixups = [
118
776
 
119
777
 
120
778
 
121
- const LiteralIntegerRe = /^(0x[0-9a-f]+|\d+)(l)?$/;
779
+
780
+ const api_LiteralIntegerRe = /^(0x[0-9a-f]+|\d+)(l)?$/;
122
781
  /*
123
782
  * This is an unfortunate hack. I want to be able to extract things
124
783
  * like the types of all of a Class's variables (in particular the type
@@ -130,7 +789,7 @@ const LiteralIntegerRe = /^(0x[0-9a-f]+|\d+)(l)?$/;
130
789
  */
131
790
 
132
791
  // Extract all enum values from api.mir
133
- async function getApiMapping(state) {
792
+ async function api_getApiMapping(state) {
134
793
  // get the path to the currently active sdk
135
794
  const parser = prettier_plugin_monkeyc_namespaceObject.parsers.monkeyc;
136
795
 
@@ -144,7 +803,7 @@ async function getApiMapping(state) {
144
803
  .replace(/^(\s*type)\s/gm, "$1def ");
145
804
 
146
805
  try {
147
- const result = collectNamespaces(parser.parse(api, {}), state);
806
+ const result = api_collectNamespaces(parser.parse(api, {}), state);
148
807
  negativeFixups.forEach((fixup) => {
149
808
  const value = fixup.split(".").reduce((state, part) => {
150
809
  const decls = state.decls[part];
@@ -169,18 +828,18 @@ async function getApiMapping(state) {
169
828
  }
170
829
  }
171
830
 
172
- function hasProperty(obj, prop) {
831
+ function api_hasProperty(obj, prop) {
173
832
  return obj && Object.prototype.hasOwnProperty.call(obj, prop);
174
833
  }
175
834
 
176
- function collectNamespaces(ast, state) {
835
+ function api_collectNamespaces(ast, state) {
177
836
  state = state || {};
178
837
  if (!state.index) state.index = {};
179
838
  if (!state.stack) {
180
839
  state.stack = [{ type: "Program", name: "$", fullName: "$" }];
181
840
  }
182
841
  const checkOne = (ns, name) => {
183
- if (hasProperty(ns.decls, name)) {
842
+ if (api_hasProperty(ns.decls, name)) {
184
843
  return ns.decls[name];
185
844
  }
186
845
  return null;
@@ -218,7 +877,7 @@ function collectNamespaces(ast, state) {
218
877
  };
219
878
 
220
879
  state.traverse = (root) =>
221
- traverseAst(
880
+ api_traverseAst(
222
881
  root,
223
882
  (node) => {
224
883
  try {
@@ -262,7 +921,7 @@ function collectNamespaces(ast, state) {
262
921
  .join(".");
263
922
  if (elm.name) {
264
923
  if (!parent.decls) parent.decls = {};
265
- if (hasProperty(parent.decls, elm.name)) {
924
+ if (api_hasProperty(parent.decls, elm.name)) {
266
925
  const what =
267
926
  node.type == "ModuleDeclaration" ? "type" : "node";
268
927
  const e = parent.decls[elm.name].find(
@@ -295,7 +954,7 @@ function collectNamespaces(ast, state) {
295
954
  case "TypedefDeclaration": {
296
955
  const [parent] = state.stack.slice(-1);
297
956
  if (!parent.decls) parent.decls = {};
298
- if (!hasProperty(parent.decls, node.id.name)) {
957
+ if (!api_hasProperty(parent.decls, node.id.name)) {
299
958
  parent.decls[node.id.name] = [];
300
959
  }
301
960
  (0,external_util_cjs_namespaceObject.pushUnique)(
@@ -308,12 +967,12 @@ function collectNamespaces(ast, state) {
308
967
  const [parent] = state.stack.slice(-1);
309
968
  if (!parent.decls) parent.decls = {};
310
969
  node.declarations.forEach((decl) => {
311
- if (!hasProperty(parent.decls, decl.id.name)) {
970
+ if (!api_hasProperty(parent.decls, decl.id.name)) {
312
971
  parent.decls[decl.id.name] = [];
313
972
  }
314
973
  if (node.kind == "const") {
315
974
  (0,external_util_cjs_namespaceObject.pushUnique)(parent.decls[decl.id.name], decl.init);
316
- if (!hasProperty(state.index, decl.id.name)) {
975
+ if (!api_hasProperty(state.index, decl.id.name)) {
317
976
  state.index[decl.id.name] = [];
318
977
  }
319
978
  (0,external_util_cjs_namespaceObject.pushUnique)(state.index[decl.id.name], parent);
@@ -334,10 +993,16 @@ function collectNamespaces(ast, state) {
334
993
  let name, init;
335
994
  if (m.type == "EnumStringMember") {
336
995
  name = m.id.name;
337
- init = m.init;
996
+ init = getLiteralNode(m.init);
997
+ if (!init) {
998
+ throw new Error("Unexpected enum initializer");
999
+ }
1000
+ if (init != m.init) {
1001
+ m.init = init;
1002
+ }
338
1003
  if (
339
1004
  init.type == "Literal" &&
340
- LiteralIntegerRe.test(init.raw)
1005
+ api_LiteralIntegerRe.test(init.raw)
341
1006
  ) {
342
1007
  prev = init.value;
343
1008
  }
@@ -358,11 +1023,11 @@ function collectNamespaces(ast, state) {
358
1023
  };
359
1024
  }
360
1025
  }
361
- if (!hasProperty(values, name)) {
1026
+ if (!api_hasProperty(values, name)) {
362
1027
  values[name] = [];
363
1028
  }
364
1029
  (0,external_util_cjs_namespaceObject.pushUnique)(values[name], init);
365
- if (!hasProperty(state.index, name)) {
1030
+ if (!api_hasProperty(state.index, name)) {
366
1031
  state.index[name] = [];
367
1032
  }
368
1033
  (0,external_util_cjs_namespaceObject.pushUnique)(state.index[name], parent);
@@ -412,7 +1077,7 @@ function collectNamespaces(ast, state) {
412
1077
  * - if post returns false, the node it was called on is
413
1078
  * removed.
414
1079
  */
415
- function traverseAst(node, pre, post) {
1080
+ function api_traverseAst(node, pre, post) {
416
1081
  const nodes = pre && pre(node);
417
1082
  if (nodes === false) return;
418
1083
  for (const key of nodes || Object.keys(node)) {
@@ -420,7 +1085,7 @@ function traverseAst(node, pre, post) {
420
1085
  if (!value) continue;
421
1086
  if (Array.isArray(value)) {
422
1087
  const deletions = value.reduce((state, obj, i) => {
423
- const repl = traverseAst(obj, pre, post);
1088
+ const repl = api_traverseAst(obj, pre, post);
424
1089
  if (repl === false) {
425
1090
  if (!state) state = {};
426
1091
  state[i] = true;
@@ -437,7 +1102,7 @@ function traverseAst(node, pre, post) {
437
1102
  );
438
1103
  }
439
1104
  } else if (typeof value == "object" && value.type) {
440
- const repl = traverseAst(value, pre, post);
1105
+ const repl = api_traverseAst(value, pre, post);
441
1106
  if (repl === false) {
442
1107
  delete node[key];
443
1108
  } else if (repl != null) {