Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  •         var info = Activity.getActivityInfo();
            var pre__1 = info != null && info.startTime != null;
            if (x == 0 && pre__1) {
                //...
            } else {
                // do something else
            }
            
    or if the inlined function doesn't use x (as here) ... then maybe even:
    
            var info = Activity.getActivityInfo();
            if (x == 0 && info != null && info.startTime != null) {
                //...
            } else {
                // do something else
            }
    
    though I agree this is something possible to do manually but a bit far-fetched to do it automatically

    But I know what will be your next question:

    What if x is a field and the inlined function also uses it and changes it and thus the order might make a difference. But maybe these cases are already excluded :)

    Actually there might be another "compromise", when not only:
    if (isInActivity()) {
    is allowed, but also:
    if (isInActivity() && foo == bar...) {

    or in other words if the inlined function is the 1st part of an expression.

  • But I know what will be your next question:

    Well, that *is* an issue, but I already check for things like that for other optimizations, such as PRE, and in order to minimize the number of local variables introduced by inlining.

    But the real issue is that in the original code, Activity.getActivityInfo() only gets called when x == 0, but in the rewrite it's always called. In this case, you and I know that it has no side effects, and it's always safe to call. But suppose it was something like dc.drawText(); or something that might crash if passed null, and the x == 0 test is what guarantees that its safe to call. So at the very least, I now need to classify every single Toybox method to say whether its safe to move it outside an if... and then perform similar analysis on user functions too (since your original code might have been var info = myInfo(); where myInfo is a non-inlined function).

    And suppose I do all of that... what if your original code had been:

            // Don't call isInActivity unless x == 0
            // for performance reasons.
            if (x == 0 && isInActivity()) {
                //...
            }

    You might not appreciate the rewrite, even though its technically safe...

    Actually there might be another "compromise", when not only:
    if (isInActivity()) {
    is allowed, but also:
    if (isInActivity() && foo == bar...) {

    Yes, that shouldn't be much harder to do. I'll take a look. There is one more issue I thought of though. The actual rewrite probably needs to be:

                var tmp;
                {
                    var info = Activity.getActivityInfo();
                    tmp = info != null && info.startTime != null;
                }
                if (tmp) {
                    // ...
                }

    rather than

                var info = Activity.getActivityInfo();
                if (info != null && info.startTime != null) {
                    // ...
                }
    

    so that info is freed before entering the body of the if statement. In this particular case, I'm not sure whether each call to Activity.getActivityInfo() allocates a new ActivityInfo, or just returns a reference to one that already exists... but in the general case the inline function might be allocating a lot of memory, and it might be important to free it before entering the if (which might *also* be allocating a lot of memory).

  • v2.0.31 is out.

    Main changes are 

    1. extensionVersion and optimizerVersion are included in build-info.json
    2. the extension will look for a local copy of @markw65/monkeyc-optimizer installed in your project directory and, if found, use that for builds rather than the one supplied with the extension.
    3. the extension displays the size of the code and data sections in the generated program binary (so you can see the effects of changes on code size and data size without having to run the simulator, and pull up the memory view).

    Point 2 allows you to pin a project to a particular optimizer version, so that if you check out an older version of your project, and rebuild it, you'll get the exact same results as the last time you built it, even if you've installed a newer version of the extension.

    To use this feature you need to install npm, and run npm init in your project directory (to create a package.json), and then npm install @markw65/monkeyc-optimizer. Then commit package.json and package-lock.json along with your project's source. You'll also want to exclude node_modules from source control (that's where the code goes; you don't need that, because npm install will faithfully restore the version of the monkeyc-optimizer specified in the package files).

    When you want to pick up a new version of the optimizer, just run npm upgrade @markw65/monkeyc-optimizer, or npm install @markw65/monkeyc-optimizer@<version>

  • v2.0.32 is out.

    This allows inlining the condition of an if-statement, and also relaxes some of the restrictions on where inlining can happen. Inlining is getting quite complex now, so I've written a wiki page describing it.

  • Very cool improvement.

    I'll throw in another old idea of mine: It would make life much easier if instead of (or maybe besides) :inline_foo we could use :inline_once. This would inline the function if it's only used once, but not inline it if it's used more than once. Is we had this feature, then using :inline (when it's always better, so even if it's called 10 times it's best) and :inline_once would cover almost all the cases I have (actually I would need to check again a few cases to see what's their status now, I think I have 1 or 2 cases when even inlining twice is useful, but 3 or more times it's not)

  • I've just uploaded v2.0.33

    This version fixes analysis for projects with barrels. Previously, although the optimizer correctly dealt with barrels, the analysis done by the extension ignored them; so symbols defined in barrels were reported as missing in the "Problems" tab. Now it should all work, including "Goto definition" and "Goto references" jumping between the main. and barrel sources.

    It also adds some analysis of project resources. Syntax errors in resource files will appear in the "Problems" tab, as will references to resources that don't exist. "Goto definition" works for resources (but only if you click on a reference in a .mc file - not references in resource files). "Goto References" only finds references in .mc files. I'm working on fleshing out the rest of this to include all references between .xml and .mc files. this isn't quite what you were asking for - but if I can get it to find all references to resources, reporting unused ones should be doable.

    A new optimization turns "X has :Y" into "false" when the compiler knows that Y doesn't exist. I'm thinking about trying to find a safe way to turn it into "true" when it knows that Y does exist; but thats often ciq version dependent, and I want to make sure it never does the wrong thing...

  • What's the safe way you use to determine that Y doesn't exist? Look at https://forums.garmin.com/developer/connect-iq/f/discussion/314411/psa-10-xx-beta-firmware-ciq-position-bug for example, it's not that clear. You probably can't even assume that a specific new symbol will never be added to an old device. I mean who knows?

  • What's the safe way you use to determine that Y doesn't exist?

    I'm going to guess by looking at {device}.api.debug.xml (e.g. devices/fr245/fr245.api.debug.xml) (which might not be foolproof).

    I would hope this would be a feature you have to explicitly opt into or which can be disabled while still using other optimization features.

  • or mir file for device

  • So it would need some kind of configuration file i.e prettier-monkey.jungle where we would be able to tell thing like (I intentionally use English and not suggesting this should be the format):

    optimize "Toybox.Application has :Properties"

    but don't optimize "Activity.Info has :currentHeartRate"

    ?

    Or you mean there would be 1 global switch to "Enable has :symbol optimization"?

    I like the idea (either one:) but again, even if we disregard that we know there are bugs in some of these files, there still is the following problem:

    How would this feature work with App Migration?

    Today I use the latest SDK with the latest devices downloaded and compile my app. Upload to the store. If I enabled the "has optimization" then when a new device comes out then will it have the feature or will it be eliminated?

    Actually I never thought about this App Migration, but _luckily_ the way I built my monkey.jungle seems to be future proof (except it isn't*):
    I define the base.excludeAnnotations and base.resourcePath such a way that it fits the "newest" group of devices (for me it's at least 32KB datafield memory and at least CIQ 3.2) and then do all the exceptions that I know (devices that came out already) that have either less memory or older CIQ or both.

    *) except that I had to add the Instinct Crossover that came out a few days ago to the exceptions, because one of the excludeAnnotations is color vs black_and_white :( So without adding it I couldn't see anything (in the simulator) because it tried to render the DF using colors.