@markw65/monkeyc-optimizer 1.0.7 → 1.0.10

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 CHANGED
@@ -56,4 +56,47 @@ More fixes found via open source projects.
56
56
  - Add support for a 'pick-one' device to aid testing
57
57
  - Add a flag to remote projects to prevent trying to build them (some projects are broken to start with)
58
58
 
59
- ---
59
+ ### 1.0.8
60
+
61
+ - Improvements
62
+
63
+ - Update to @markw65/prettier-plugin-monkeyc:1.0.14
64
+ - Parse and respect \<build\> instructions in resource files
65
+ - Add minimal barrel support
66
+ - Better checking for whether the optimized source is up to date
67
+ - Rename locals which would be marked re-declaring
68
+
69
+ - Bug Fixes
70
+
71
+ - Generate the default jungle dynamically, since sdk/bin/default.jungle is generated lazily, and may not exist in newly installed sdks, or may be out of date after device installations.
72
+ - Fix a bug generating language settings in optimized jungle
73
+ - Fix a bug introduced by pick-one: don't modify a shared array
74
+ - Don't allow src paths to navigate out of the optimized directory
75
+ - Fix some windows paths issues
76
+
77
+ - Tests
78
+ - More parallelism while fetching remote projects for testing
79
+ - Add option to build the original project, rather than the optimized one
80
+ - Add support for overriding build options on a per project basis
81
+ - Add an option so we only 'fix' the manifest when running remote projects
82
+ - Check the manifest's application id, and throw in a valid one if necessary
83
+ - Allow project specific overrides for the generated monkey.jungle files, and use it to fix some projects
84
+ - Add patches for some broken projects
85
+
86
+ ### 1.0.9
87
+
88
+ - Only generate the parts of the jungle we're going to use
89
+ - Also publish sdk-util.cjs
90
+ - Bump to @markw65/prettier-plugin-monkeyc:1.0.15
91
+ - Fixes a bug that dropped attributes on modules
92
+ - LiteralIntegerRe should be case insensitive
93
+ - Proper fix for promiseAll
94
+ - Auto-include barrels.jungle when its present
95
+
96
+ ### 1.0.10
97
+
98
+ - Add --execute option to test.js to run the projects after building them
99
+ - Add support for optimizing barrels
100
+ - Add some typing via jsdoc, and turn on ts validation in vscode
101
+ - Bump to @markw65/prettier-plugin-monkeyc:1.0.16 so ts recognizes its exports
102
+ - Add [garmin/connectiq-apps](https://github.com/garmin/connectiq-apps) and fix some minor issues it revealed
package/build/api.cjs CHANGED
@@ -54,66 +54,10 @@ const prettier_plugin_monkeyc_namespaceObject = require("@markw65/prettier-plugi
54
54
  const promises_namespaceObject = require("fs/promises");
55
55
  ;// CONCATENATED MODULE: external "prettier/standalone.js"
56
56
  const standalone_js_namespaceObject = require("prettier/standalone.js");
57
- ;// CONCATENATED MODULE: external "./util.cjs"
58
- const external_util_cjs_namespaceObject = require("./util.cjs");
59
- ;// CONCATENATED MODULE: ./src/negative-fixups.js
60
- /*
61
- * This is strange. It pretty much has to be a bug in the sdk,
62
- * but its the same in every sdk.
63
- *
64
- * ${sdk}/bin/api.mir describes the Toybox api down to every module,
65
- * class, function, enum and constant, together with the enum
66
- * and constant values.
67
- *
68
- * The problem is that every enum or constant with a negative
69
- * value, appears with the corresponding positive value in api.mir.
70
- * So eg Graphics.COLOR_TRANSPARENT should be -1, but instead, it has
71
- * the value 1.
72
- *
73
- * This is a (currently somewhat ad-hoc) list of the negative constants
74
- * so we can fix them after reading api.mir.
75
- */
76
- const negativeFixups = [
77
- "Toybox.Communications.BLE_CONNECTION_UNAVAILABLE",
78
- "Toybox.Communications.INVALID_HTTP_BODY_IN_REQUEST",
79
- "Toybox.Communications.REQUEST_CANCELLED",
80
- "Toybox.Communications.UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE",
81
- "Toybox.Communications.UNABLE_TO_PROCESS_IMAGE",
82
- "Toybox.Communications.NETWORK_RESPONSE_OUT_OF_MEMORY",
83
- "Toybox.Communications.BLE_REQUEST_CANCELLED",
84
- "Toybox.Communications.INVALID_HTTP_METHOD_IN_REQUEST",
85
- "Toybox.Communications.BLE_NO_DATA",
86
- "Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_REQUEST",
87
- "Toybox.Communications.BLE_ERROR",
88
- "Toybox.Communications.NETWORK_RESPONSE_TOO_LARGE",
89
- "Toybox.Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE",
90
- "Toybox.Communications.BLE_REQUEST_TOO_LARGE",
91
- "Toybox.Communications.UNABLE_TO_PROCESS_MEDIA",
92
- "Toybox.Communications.REQUEST_CONNECTION_DROPPED",
93
- "Toybox.Communications.BLE_UNKNOWN_SEND_ERROR",
94
- "Toybox.Communications.BLE_QUEUE_FULL",
95
- "Toybox.Communications.STORAGE_FULL",
96
- "Toybox.Communications.BLE_SERVER_TIMEOUT",
97
- "Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_NETWORK_RESPONSE",
98
- "Toybox.Communications.SECURE_CONNECTION_REQUIRED",
99
- "Toybox.Communications.NETWORK_REQUEST_TIMED_OUT",
100
- "Toybox.Communications.BLE_HOST_TIMEOUT",
101
- "Toybox.Communications.UNABLE_TO_PROCESS_HLS",
102
- "Toybox.Graphics.COLOR_TRANSPARENT",
103
- "Toybox.AntPlus.INVALID_SPEED",
104
- "Toybox.AntPlus.INVALID_CADENCE",
105
- "Toybox.WatchUi.LAYOUT_VALIGN_START",
106
- "Toybox.WatchUi.LAYOUT_VALIGN_TOP",
107
- "Toybox.WatchUi.LAYOUT_VALIGN_BOTTOM",
108
- "Toybox.WatchUi.LAYOUT_VALIGN_CENTER",
109
- "Toybox.WatchUi.LAYOUT_HALIGN_RIGHT",
110
- "Toybox.WatchUi.LAYOUT_HALIGN_CENTER",
111
- "Toybox.WatchUi.LAYOUT_HALIGN_START",
112
- "Toybox.WatchUi.LAYOUT_HALIGN_LEFT",
113
- ];
114
-
115
57
  ;// CONCATENATED MODULE: external "./api.cjs"
116
58
  const external_api_cjs_namespaceObject = require("./api.cjs");
59
+ ;// CONCATENATED MODULE: external "./util.cjs"
60
+ const external_util_cjs_namespaceObject = require("./util.cjs");
117
61
  ;// CONCATENATED MODULE: ./src/mc-rewrite.js
118
62
 
119
63
 
@@ -168,12 +112,8 @@ function collectClassInfo(state) {
168
112
  });
169
113
  }
170
114
 
171
- async function analyze(fileNames, buildConfig) {
172
- const excludeAnnotations =
173
- buildConfig && buildConfig.excludeAnnotations
174
- ? Object.fromEntries(buildConfig.excludeAnnotations.map((a) => [a, true]))
175
- : {};
176
-
115
+ async function analyze(fnMap) {
116
+ let excludeAnnotations;
177
117
  const allImports = [];
178
118
  const state = {
179
119
  allFunctions: [],
@@ -181,11 +121,11 @@ async function analyze(fileNames, buildConfig) {
181
121
  shouldExclude(node) {
182
122
  if (node.attrs && node.attrs.attrs) {
183
123
  if (
184
- node.attrs.attrs.filter((attr) => {
124
+ node.attrs.attrs.some((attr) => {
185
125
  if (attr.type != "UnaryExpression") return false;
186
126
  if (attr.argument.type != "Identifier") return false;
187
127
  return hasProperty(excludeAnnotations, attr.argument.name);
188
- }).length
128
+ })
189
129
  ) {
190
130
  return true;
191
131
  }
@@ -228,25 +168,29 @@ async function analyze(fileNames, buildConfig) {
228
168
  };
229
169
  markApi(state.stack[0]);
230
170
 
171
+ const getAst = (source, monkeyCSource, exclude) => {
172
+ excludeAnnotations = exclude;
173
+ const ast = MonkeyC.parsers.monkeyc.parse(monkeyCSource, {
174
+ grammarSource: source,
175
+ });
176
+ ast.source = source;
177
+ ast.monkeyCSource = monkeyCSource;
178
+ collectNamespaces(ast, state);
179
+ return ast;
180
+ };
231
181
  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
- }))
182
+ Object.entries(fnMap).map(([name, { excludeAnnotations }]) =>
183
+ fs.readFile(name).then((data) => ({
184
+ name,
185
+ ast: getAst(
186
+ name,
187
+ data.toString().replace(/\r\n/g, "\n"),
188
+ excludeAnnotations
189
+ ),
190
+ }))
191
+ )
238
192
  );
239
193
 
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
194
  delete state.shouldExclude;
251
195
  delete state.post;
252
196
 
@@ -467,8 +411,8 @@ function evaluateFunction(func, args) {
467
411
  }
468
412
  }
469
413
 
470
- async function optimizeMonkeyC(fileNames, buildConfig) {
471
- const { files, state } = await analyze(fileNames, buildConfig);
414
+ async function optimizeMonkeyC(fnMap) {
415
+ const { files, state } = await analyze(fnMap);
472
416
  const replace = (node, obj) => {
473
417
  for (const k of Object.keys(node)) {
474
418
  delete node[k];
@@ -529,6 +473,7 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
529
473
  (sc.superClass && checkInherited(sc, name))
530
474
  );
531
475
 
476
+ state.localsStack = [{}];
532
477
  state.exposed = {};
533
478
  state.calledFunctions = {};
534
479
  state.pre = (node) => {
@@ -578,8 +523,74 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
578
523
 
579
524
  case "EnumDeclaration":
580
525
  return false;
581
- case "VariableDeclarator":
526
+ case "ForStatement": {
527
+ const map = state.localsStack.slice(-1).pop().map;
528
+ if (map) {
529
+ state.localsStack.push({ node, map: { ...map } });
530
+ }
531
+ break;
532
+ }
533
+ case "VariableDeclarator": {
534
+ const locals = state.localsStack.slice(-1).pop();
535
+ const { map } = locals;
536
+ if (map) {
537
+ if (hasProperty(map, node.id.name)) {
538
+ // We already have a variable with this name in scope
539
+ // Recent monkeyc compilers complain, so rename it
540
+ let suffix = 0;
541
+ let node_name = node.id.name;
542
+ const match = node_name.match(/^pmcr_(.*)_(\d+)$/);
543
+ if (match) {
544
+ node_name = match[1];
545
+ suffix = parseInt(match[2], 10) + 1;
546
+ }
547
+ if (!locals.inners) {
548
+ // find all the names declared in this scope, to avoid
549
+ // more conflicts
550
+ locals.inners = {};
551
+ traverseAst(locals.node, (node) => {
552
+ if (node.type === "VariableDeclarator") {
553
+ locals.inners[node.id.name] = true;
554
+ }
555
+ });
556
+ }
557
+ let name;
558
+ while (true) {
559
+ name = `pmcr_${node_name}_${suffix}`;
560
+ if (
561
+ !hasProperty(map, name) &&
562
+ !hasProperty(locals.inners, name)
563
+ ) {
564
+ // we also need to ensure that we don't hide the name of
565
+ // an outer module, class, function, enum or variable,
566
+ // since someone might want to access it from this scope.
567
+ let ok = false;
568
+ let i;
569
+ for (i = state.stack.length; i--; ) {
570
+ const elm = state.stack[i];
571
+ if (ok) {
572
+ if (hasProperty(elm.decls, name)) {
573
+ break;
574
+ }
575
+ } else if (elm.node.type === "FunctionDeclaration") {
576
+ ok = true;
577
+ }
578
+ }
579
+ if (i < 0) {
580
+ break;
581
+ }
582
+ }
583
+ suffix++;
584
+ }
585
+ map[node.id.name] = name;
586
+ map[name] = true;
587
+ node.id.name = name;
588
+ } else {
589
+ map[node.id.name] = true;
590
+ }
591
+ }
582
592
  return ["init"];
593
+ }
583
594
  case "UnaryExpression":
584
595
  if (node.operator == ":") {
585
596
  // If we produce a Symbol, for a given name,
@@ -594,6 +605,15 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
594
605
  }
595
606
  break;
596
607
  case "Identifier": {
608
+ const map = state.localsStack.slice(-1).pop().map;
609
+ if (map) {
610
+ if (hasProperty(map, node.name)) {
611
+ const name = map[node.name];
612
+ if (name !== true) {
613
+ node.name = name;
614
+ }
615
+ }
616
+ }
597
617
  if (hasProperty(state.index, node.name)) {
598
618
  if (!lookupAndReplace(node)) {
599
619
  state.exposed[node.name] = true;
@@ -614,7 +634,20 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
614
634
  return ["object"];
615
635
  }
616
636
  break;
637
+ case "BlockStatement": {
638
+ const map = state.localsStack.slice(-1).pop().map;
639
+ if (map) {
640
+ state.localsStack.push({
641
+ node,
642
+ map: { ...map },
643
+ });
644
+ }
645
+ break;
646
+ }
617
647
  case "FunctionDeclaration": {
648
+ const map = {};
649
+ node.params && node.params.forEach((p) => (map[p.name] = true));
650
+ state.localsStack.push({ node, map });
618
651
  const [parent] = state.stack.slice(-2);
619
652
  if (parent.type == "ClassDeclaration" && !maybeCalled(node)) {
620
653
  let used = false;
@@ -634,6 +667,9 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
634
667
  }
635
668
  };
636
669
  state.post = (node) => {
670
+ if (state.localsStack.slice(-1).pop().node === node) {
671
+ state.localsStack.pop();
672
+ }
637
673
  const opt = optimizeNode(node);
638
674
  if (opt) {
639
675
  replace(node, opt);
@@ -769,6 +805,64 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
769
805
  return files;
770
806
  }
771
807
 
808
+ ;// CONCATENATED MODULE: ./src/negative-fixups.js
809
+ /*
810
+ * This is strange. It pretty much has to be a bug in the sdk,
811
+ * but its the same in every sdk.
812
+ *
813
+ * ${sdk}/bin/api.mir describes the Toybox api down to every module,
814
+ * class, function, enum and constant, together with the enum
815
+ * and constant values.
816
+ *
817
+ * The problem is that every enum or constant with a negative
818
+ * value, appears with the corresponding positive value in api.mir.
819
+ * So eg Graphics.COLOR_TRANSPARENT should be -1, but instead, it has
820
+ * the value 1.
821
+ *
822
+ * This is a (currently somewhat ad-hoc) list of the negative constants
823
+ * so we can fix them after reading api.mir.
824
+ */
825
+ const negativeFixups = [
826
+ "Toybox.Communications.BLE_CONNECTION_UNAVAILABLE",
827
+ "Toybox.Communications.INVALID_HTTP_BODY_IN_REQUEST",
828
+ "Toybox.Communications.REQUEST_CANCELLED",
829
+ "Toybox.Communications.UNSUPPORTED_CONTENT_TYPE_IN_RESPONSE",
830
+ "Toybox.Communications.UNABLE_TO_PROCESS_IMAGE",
831
+ "Toybox.Communications.NETWORK_RESPONSE_OUT_OF_MEMORY",
832
+ "Toybox.Communications.BLE_REQUEST_CANCELLED",
833
+ "Toybox.Communications.INVALID_HTTP_METHOD_IN_REQUEST",
834
+ "Toybox.Communications.BLE_NO_DATA",
835
+ "Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_REQUEST",
836
+ "Toybox.Communications.BLE_ERROR",
837
+ "Toybox.Communications.NETWORK_RESPONSE_TOO_LARGE",
838
+ "Toybox.Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE",
839
+ "Toybox.Communications.BLE_REQUEST_TOO_LARGE",
840
+ "Toybox.Communications.UNABLE_TO_PROCESS_MEDIA",
841
+ "Toybox.Communications.REQUEST_CONNECTION_DROPPED",
842
+ "Toybox.Communications.BLE_UNKNOWN_SEND_ERROR",
843
+ "Toybox.Communications.BLE_QUEUE_FULL",
844
+ "Toybox.Communications.STORAGE_FULL",
845
+ "Toybox.Communications.BLE_SERVER_TIMEOUT",
846
+ "Toybox.Communications.INVALID_HTTP_HEADER_FIELDS_IN_NETWORK_RESPONSE",
847
+ "Toybox.Communications.SECURE_CONNECTION_REQUIRED",
848
+ "Toybox.Communications.NETWORK_REQUEST_TIMED_OUT",
849
+ "Toybox.Communications.BLE_HOST_TIMEOUT",
850
+ "Toybox.Communications.UNABLE_TO_PROCESS_HLS",
851
+ "Toybox.Graphics.COLOR_TRANSPARENT",
852
+ "Toybox.AntPlus.INVALID_SPEED",
853
+ "Toybox.AntPlus.INVALID_CADENCE",
854
+ "Toybox.WatchUi.LAYOUT_VALIGN_START",
855
+ "Toybox.WatchUi.LAYOUT_VALIGN_TOP",
856
+ "Toybox.WatchUi.LAYOUT_VALIGN_BOTTOM",
857
+ "Toybox.WatchUi.LAYOUT_VALIGN_CENTER",
858
+ "Toybox.WatchUi.LAYOUT_HALIGN_RIGHT",
859
+ "Toybox.WatchUi.LAYOUT_HALIGN_CENTER",
860
+ "Toybox.WatchUi.LAYOUT_HALIGN_START",
861
+ "Toybox.WatchUi.LAYOUT_HALIGN_LEFT",
862
+ ];
863
+
864
+ ;// CONCATENATED MODULE: external "./sdk-util.cjs"
865
+ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
772
866
  ;// CONCATENATED MODULE: ./src/api.js
773
867
 
774
868
 
@@ -777,7 +871,8 @@ async function optimizeMonkeyC(fileNames, buildConfig) {
777
871
 
778
872
 
779
873
 
780
- const api_LiteralIntegerRe = /^(0x[0-9a-f]+|\d+)(l)?$/;
874
+
875
+ const api_LiteralIntegerRe = /^(0x[0-9a-f]+|\d+)(l)?$/i;
781
876
  /*
782
877
  * This is an unfortunate hack. I want to be able to extract things
783
878
  * like the types of all of a Class's variables (in particular the type
@@ -793,7 +888,7 @@ async function api_getApiMapping(state) {
793
888
  // get the path to the currently active sdk
794
889
  const parser = prettier_plugin_monkeyc_namespaceObject.parsers.monkeyc;
795
890
 
796
- const sdk = await (0,external_util_cjs_namespaceObject.getSdkPath)();
891
+ const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
797
892
 
798
893
  const api = (await promises_namespaceObject.readFile(`${sdk}bin/api.mir`))
799
894
  .toString()