@markw65/monkeyc-optimizer 1.1.14 → 1.1.15

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
@@ -726,3 +726,14 @@ Bug Fixes
726
726
  - Remove stores to dead locals
727
727
  - Remove side-effect free code that produces an unused result
728
728
  - Optimize shift left by constant to multiply, since the bytecode is smaller (this seems to be a bug in the garmin tools; they consider shift left and shift right to have an 8-bit argument, but its always zero, and the simulator and devices treat it as a one byte shift instruction, followed by a one byte nop).
729
+
730
+ ### 1.1.15
731
+
732
+ - Post build optimizer improvements
733
+
734
+ - Simplify LogicalExpressions. This generally saves 3 bytes per `&&` or `||`, and also makes them faster
735
+ - Adds a simple code sharing pass. If multiple code paths converge to the same point (or leave the function via return) and they end with the same sequence of bytecode, they're merged into one.
736
+ - Flips branch-true to branch-false or vice versa if the fall through block has multiple predecessors, and the target block has just one. This often leads to better control flow, reducing the number of "goto" bytecodes required.
737
+
738
+ - Source to Source Optimizer improvements
739
+ - Adds an `Iterate Optimizer` option that causes the optimizer to keep re-running until it finds nothing to remove. Defaults to false.
package/build/api.cjs CHANGED
@@ -3234,33 +3234,6 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3234
3234
  return result;
3235
3235
  };
3236
3236
  const topLocals = () => state.localsStack[state.localsStack.length - 1];
3237
- /*
3238
- * Might this function be called from somewhere, including
3239
- * callbacks from the api (eg getSettingsView, etc).
3240
- */
3241
- const maybeCalled = (func) => {
3242
- if (!func.body) {
3243
- // this is an api.mir function. It can be called
3244
- return true;
3245
- }
3246
- if (hasProperty(state.exposed, func.id.name))
3247
- return true;
3248
- if (func.attrs &&
3249
- func.attrs.attributes &&
3250
- func.attrs.attributes.elements.some((attr) => {
3251
- if (attr.type !== "UnaryExpression")
3252
- return false;
3253
- if (attr.argument.type !== "Identifier")
3254
- return false;
3255
- return attr.argument.name === "test";
3256
- })) {
3257
- return true;
3258
- }
3259
- if (hasProperty(state.calledFunctions, func.id.name)) {
3260
- return (state.calledFunctions[func.id.name].find((f) => f === func) !== null);
3261
- }
3262
- return false;
3263
- };
3264
3237
  /*
3265
3238
  * Does elm (a class) have a maybeCalled function called name,
3266
3239
  * anywhere in its superClass chain.
@@ -3270,7 +3243,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3270
3243
  elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
3271
3244
  sc.decls[name].some((f) => isStateNode(f) &&
3272
3245
  f.type === "FunctionDeclaration" &&
3273
- maybeCalled(f.node))) ||
3246
+ maybeCalled(state, f.node))) ||
3274
3247
  (sc.superClass && checkInherited(sc, name))));
3275
3248
  const renamer = (idnode) => {
3276
3249
  const ident = idnode.type === "Identifier" ? idnode : idnode.left;
@@ -3471,7 +3444,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3471
3444
  }
3472
3445
  istate = is;
3473
3446
  }
3474
- if (parent.type === "ClassDeclaration" && !maybeCalled(node)) {
3447
+ if (parent.type === "ClassDeclaration" && !maybeCalled(state, node)) {
3475
3448
  let used = false;
3476
3449
  if (node.id.name === "initialize") {
3477
3450
  used = true;
@@ -3673,15 +3646,31 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3673
3646
  Object.values(fnMap).forEach((f) => {
3674
3647
  collectNamespaces(f.ast, state);
3675
3648
  });
3676
- state.usedByName = {};
3677
- state.calledFunctions = {};
3678
- state.exposed = state.nextExposed;
3679
- state.nextExposed = {};
3680
- Object.values(fnMap).forEach((f) => {
3681
- collectNamespaces(f.ast, state);
3682
- });
3683
- state.exposed = state.nextExposed;
3684
- state.nextExposed = {};
3649
+ const cleanupAll = () => Object.values(fnMap).reduce((changes, f) => {
3650
+ traverseAst(f.ast, undefined, (node) => {
3651
+ const ret = cleanup(state, node, f.ast);
3652
+ if (ret === false) {
3653
+ changes = true;
3654
+ state.removeNodeComments(node, f.ast);
3655
+ }
3656
+ else if (ret) {
3657
+ changes = true;
3658
+ }
3659
+ return ret;
3660
+ });
3661
+ return changes;
3662
+ }, false);
3663
+ do {
3664
+ state.usedByName = {};
3665
+ state.calledFunctions = {};
3666
+ state.exposed = state.nextExposed;
3667
+ state.nextExposed = {};
3668
+ Object.values(fnMap).forEach((f) => {
3669
+ collectNamespaces(f.ast, state);
3670
+ });
3671
+ state.exposed = state.nextExposed;
3672
+ state.nextExposed = {};
3673
+ } while (cleanupAll() && state.config?.iterateOptimizer);
3685
3674
  delete state.pre;
3686
3675
  delete state.post;
3687
3676
  if (state.config?.minimizeModules ?? true) {
@@ -3690,108 +3679,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3690
3679
  });
3691
3680
  }
3692
3681
  Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
3693
- const cleanup = (node) => {
3694
- switch (node.type) {
3695
- case "ThisExpression":
3696
- node.text = "self";
3697
- break;
3698
- case "EnumStringBody":
3699
- if (node.members.every((m) => {
3700
- const name = "name" in m ? m.name : m.id.name;
3701
- return (hasProperty(state.index, name) &&
3702
- !hasProperty(state.exposed, name) &&
3703
- !hasProperty(state.usedByName, name));
3704
- })) {
3705
- node.enumType = [
3706
- ...new Set(node.members.map((m) => {
3707
- if (!("init" in m))
3708
- return "Number";
3709
- const [node, type] = getNodeValue(m.init);
3710
- if (!node) {
3711
- throw new Error("Failed to get type for eliminated enum");
3712
- }
3713
- return type;
3714
- })),
3715
- ].join(" or ");
3716
- node.members.splice(0);
3717
- }
3718
- break;
3719
- case "EnumDeclaration":
3720
- if (!node.body.members.length) {
3721
- if (!node.id)
3722
- return false;
3723
- if (!node.body.enumType) {
3724
- throw new Error("Missing enumType on optimized enum");
3725
- }
3726
- return {
3727
- type: "TypedefDeclaration",
3728
- id: node.id,
3729
- ts: {
3730
- type: "UnaryExpression",
3731
- argument: {
3732
- type: "TypeSpecList",
3733
- ts: [
3734
- node.body.enumType,
3735
- ],
3736
- },
3737
- prefix: true,
3738
- operator: " as",
3739
- },
3740
- };
3741
- }
3742
- break;
3743
- case "VariableDeclaration": {
3744
- node.declarations = node.declarations.filter((d) => {
3745
- const name = variableDeclarationName(d.id);
3746
- return (!hasProperty(state.index, name) ||
3747
- hasProperty(state.exposed, name) ||
3748
- hasProperty(state.usedByName, name));
3749
- });
3750
- if (!node.declarations.length) {
3751
- return false;
3752
- }
3753
- break;
3754
- }
3755
- case "ClassElement":
3756
- if (!node.item) {
3757
- return false;
3758
- }
3759
- break;
3760
- case "FunctionDeclaration":
3761
- if (!maybeCalled(node)) {
3762
- if (node.attrs &&
3763
- node.attrs.attributes &&
3764
- node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
3765
- break;
3766
- }
3767
- return false;
3768
- }
3769
- break;
3770
- case "ClassDeclaration":
3771
- case "ModuleDeclaration":
3772
- // none of the attributes means anything on classes and
3773
- // modules, and the new compiler complains about some
3774
- // of them. Just drop them all.
3775
- if (node.attrs && node.attrs.access) {
3776
- if (node.attrs.attributes) {
3777
- delete node.attrs.access;
3778
- }
3779
- else {
3780
- delete node.attrs;
3781
- }
3782
- }
3783
- }
3784
- return null;
3785
- };
3786
- Object.entries(fnMap).forEach(([, f]) => {
3787
- traverseAst(f.ast, undefined, (node) => {
3788
- const ret = cleanup(node);
3789
- if (ret === false) {
3790
- state.removeNodeComments(node, f.ast);
3791
- }
3792
- return ret;
3793
- });
3794
- });
3682
+ cleanupAll();
3795
3683
  config.checkCompilerLookupRules = checkLookupRules;
3796
3684
  reportMissingSymbols(state, config);
3797
3685
  if (state.inlineDiagnostics) {
@@ -3817,6 +3705,129 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
3817
3705
  });
3818
3706
  return state.diagnostics;
3819
3707
  }
3708
+ /*
3709
+ * Might this function be called from somewhere, including
3710
+ * callbacks from the api (eg getSettingsView, etc).
3711
+ */
3712
+ function maybeCalled(state, func) {
3713
+ if (!func.body) {
3714
+ // this is an api.mir function. It can be called
3715
+ return true;
3716
+ }
3717
+ if (hasProperty(state.exposed, func.id.name))
3718
+ return true;
3719
+ if (func.attrs &&
3720
+ func.attrs.attributes &&
3721
+ func.attrs.attributes.elements.some((attr) => {
3722
+ if (attr.type !== "UnaryExpression")
3723
+ return false;
3724
+ if (attr.argument.type !== "Identifier")
3725
+ return false;
3726
+ return attr.argument.name === "test";
3727
+ })) {
3728
+ return true;
3729
+ }
3730
+ if (hasProperty(state.calledFunctions, func.id.name)) {
3731
+ return state.calledFunctions[func.id.name].find((f) => f === func) !== null;
3732
+ }
3733
+ return false;
3734
+ }
3735
+ function cleanup(state, node, ast) {
3736
+ switch (node.type) {
3737
+ case "ThisExpression":
3738
+ node.text = "self";
3739
+ break;
3740
+ case "EnumStringBody":
3741
+ if (node.members.every((m) => {
3742
+ const name = "name" in m ? m.name : m.id.name;
3743
+ return (hasProperty(state.index, name) &&
3744
+ !hasProperty(state.exposed, name) &&
3745
+ !hasProperty(state.usedByName, name));
3746
+ })) {
3747
+ node.enumType = [
3748
+ ...new Set(node.members.map((m) => {
3749
+ if (!("init" in m))
3750
+ return "Number";
3751
+ const [node, type] = getNodeValue(m.init);
3752
+ if (!node) {
3753
+ throw new Error("Failed to get type for eliminated enum");
3754
+ }
3755
+ return type;
3756
+ })),
3757
+ ].join(" or ");
3758
+ node.members.splice(0);
3759
+ }
3760
+ break;
3761
+ case "EnumDeclaration":
3762
+ if (!node.body.members.length) {
3763
+ if (!node.id)
3764
+ return false;
3765
+ if (!node.body.enumType) {
3766
+ throw new Error("Missing enumType on optimized enum");
3767
+ }
3768
+ state.removeNodeComments(node, ast);
3769
+ return withLocDeep({
3770
+ type: "TypedefDeclaration",
3771
+ id: node.id,
3772
+ ts: {
3773
+ type: "UnaryExpression",
3774
+ argument: {
3775
+ type: "TypeSpecList",
3776
+ ts: [
3777
+ node.body.enumType,
3778
+ ],
3779
+ },
3780
+ prefix: true,
3781
+ operator: " as",
3782
+ },
3783
+ }, node, node);
3784
+ }
3785
+ break;
3786
+ case "VariableDeclarator": {
3787
+ const name = variableDeclarationName(node.id);
3788
+ return !hasProperty(state.index, name) ||
3789
+ hasProperty(state.exposed, name) ||
3790
+ hasProperty(state.usedByName, name)
3791
+ ? null
3792
+ : false;
3793
+ }
3794
+ case "VariableDeclaration": {
3795
+ if (!node.declarations.length) {
3796
+ return false;
3797
+ }
3798
+ break;
3799
+ }
3800
+ case "ClassElement":
3801
+ if (!node.item) {
3802
+ return false;
3803
+ }
3804
+ break;
3805
+ case "FunctionDeclaration":
3806
+ if (!maybeCalled(state, node)) {
3807
+ if (node.attrs &&
3808
+ node.attrs.attributes &&
3809
+ node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
3810
+ break;
3811
+ }
3812
+ return false;
3813
+ }
3814
+ break;
3815
+ case "ClassDeclaration":
3816
+ case "ModuleDeclaration":
3817
+ // none of the attributes means anything on classes and
3818
+ // modules, and the new compiler complains about some
3819
+ // of them. Just drop them all.
3820
+ if (node.attrs && node.attrs.access) {
3821
+ if (node.attrs.attributes) {
3822
+ delete node.attrs.access;
3823
+ }
3824
+ else {
3825
+ delete node.attrs;
3826
+ }
3827
+ }
3828
+ }
3829
+ return null;
3830
+ }
3820
3831
  function optimizeCall(istate, node, context) {
3821
3832
  const state = istate.state;
3822
3833
  const [name, results] = state.lookupNonlocal(node.callee);
@@ -12356,6 +12367,14 @@ module.exports = require("./api.cjs");
12356
12367
 
12357
12368
  /***/ }),
12358
12369
 
12370
+ /***/ 7168:
12371
+ /***/ ((module) => {
12372
+
12373
+ "use strict";
12374
+ module.exports = require("./sdk-util.cjs");
12375
+
12376
+ /***/ }),
12377
+
12359
12378
  /***/ 6906:
12360
12379
  /***/ ((module) => {
12361
12380
 
@@ -12515,8 +12534,8 @@ var negative_fixups = __webpack_require__(4499);
12515
12534
  var optimizer_types = __webpack_require__(9697);
12516
12535
  // EXTERNAL MODULE: external "./api.cjs"
12517
12536
  var external_api_cjs_ = __webpack_require__(6817);
12518
- ;// CONCATENATED MODULE: external "./sdk-util.cjs"
12519
- const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
12537
+ // EXTERNAL MODULE: external "./sdk-util.cjs"
12538
+ var external_sdk_util_cjs_ = __webpack_require__(7168);
12520
12539
  ;// CONCATENATED MODULE: ./src/resources.ts
12521
12540
 
12522
12541
 
@@ -12568,7 +12587,7 @@ function visit_resources(elements, parent, v) {
12568
12587
  error(e);
12569
12588
  break;
12570
12589
  }
12571
- visit_resources(external_sdk_util_cjs_namespaceObject.xmlUtil.elementKids(e), e, visitor);
12590
+ visit_resources(external_sdk_util_cjs_.xmlUtil.elementKids(e), e, visitor);
12572
12591
  break;
12573
12592
  // These are the resources themselves. Some can occur at top level; most
12574
12593
  // are restricted to <resources> or one or more of the specific lists above
@@ -12713,7 +12732,7 @@ function add_resources_to_ast(state, ast, resources, manifestXML) {
12713
12732
  rez.body.body.push(hiddenRez);
12714
12733
  if (barrel === "" &&
12715
12734
  manifestXML &&
12716
- manifestXML.body instanceof external_sdk_util_cjs_namespaceObject.xmlUtil.Nodes) {
12735
+ manifestXML.body instanceof external_sdk_util_cjs_.xmlUtil.Nodes) {
12717
12736
  manifestXML.body
12718
12737
  .children("iq:application")
12719
12738
  .elements.forEach((e) => add_one_resource(state, manifestXML, rez, e));
@@ -12856,7 +12875,7 @@ function visit_resource_refs(state, doc, e) {
12856
12875
  return;
12857
12876
  }
12858
12877
  };
12859
- external_sdk_util_cjs_namespaceObject.xmlUtil.visit_xml([e], {
12878
+ external_sdk_util_cjs_.xmlUtil.visit_xml([e], {
12860
12879
  pre(node) {
12861
12880
  if (node.type !== "element")
12862
12881
  return false;
@@ -13301,7 +13320,7 @@ function checkCompilerVersion(version, sdkVer) {
13301
13320
  async function getApiMapping(state, resourcesMap, manifestXML) {
13302
13321
  // get the path to the currently active sdk
13303
13322
  const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
13304
- const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
13323
+ const sdk = await (0,external_sdk_util_cjs_.getSdkPath)();
13305
13324
  if (state) {
13306
13325
  state.sdk = sdk;
13307
13326
  const match = state.sdk?.match(/-(\d+\.\d+\.\d+)/);