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]

  • Is anyone seeing any benefits from "Minimize Modules" option?

    It's typically never done very much, unless you were in the habit of not importing things and just doing '$.Toybox.Application.whatever'.

    But there is one fairly common case it still helps with. If you do:

    import Toybox.Application;
    
    ...
    
       Application.Properties.setValue(...);
       Properties.setValue(...);
    

    In both cases, Properties refers to Application.Properties. But Application.Properties has not been imported, so the generated code (without my optimizer) gets the Toybox.Application module, then looks up Properties, then looks up setValue.

    If you add an import of Toybox.Application.Properties, and use the second form, then the generated code will just get the Properties module, and lookup setValue (with 7.x, and system 7 devices, that's a single instruction). And thats what MinimizeModules does.

    I just checked and neither 6.4.2 nor 7.1.1 gets it right without the optimizer rewriting it.

    But yes, it's likely to be a pretty small win.

  • Yes, thanks.

    I've seen Application.Properties example on your github wiki. It still works for Toybox modules.

    It looks like something is changed for non-Toybox modules, like Rez.

    For SDK 4.2.4 (nothing is special about this particular version, it is just not too old and not too new version) the following function:

    import Toybox.System;
    // ...
    private function printName() as Void {
        System.println(Rez.Strings.AppName);
    }

    has the following bytecode:

        argc 1
        spush Toybox_System
        getm
        spush println
        getv
        frpush
        spush globals
        getm
        spush Rez
        getv
        spush Strings
        getv
        spush AppName
        getv
        invokem 2
        popv
        return

    With "using Rez;" (that's what optimizer adds if this option is enabled) bytecode is a little bit shorter:

        argc 1
        spush Toybox_System
        getm
        spush println
        getv
        frpush
        spush globals/Rez
        getm
        spush Strings
        getv
        spush AppName
        getv
        invokem 2
        popv
        return

    as it has only "spush globals/Rez" instead of "spush globals; getm; spush Rez", and the code size is smaller.

    For SDK 6.4.2 bytecode with "using Rez;" and without it is always like "spush globals; getm; spush Rez".

    The point of my investigations is to find a way of reducing code size for functions that heavily use localized strings in "Rez.Strings.SomeString" form, and I thought that this optimization should help. I know about such tricks as "Rez.Strings[symbol]" helper function and "var strings = Rez.Strings; var str1 = strings.SomeString; var str2 = strings.SomeOtherString;" but they all lack compile time checks to ensure SomeString exists.

  • Interesting. Regarding the compile time check, if you just use the regular Rez.Strings.foo and let the optimizer generate the optimized code with the tricks you mentioned (I did not check if it does, but if not then maybe it can be added) then it should work. But I am also interested in finding a way to also do some "manual" optimizations that help even when the optimizer isn't used.

  • It looks like something is changed for non-Toybox modules, like Rez.

    You're right. I think its Garmin's fix for this bug I filed.

    It's a shame, because my optimizer checks to see if the bug might be relevant, and adds the import when it isn't. But Garmin's fix is to never load Rez directly, and always load it via $.Rez, even when there's no background or glance code.

    I think it's something I could fix in the post build optimizer, but that's probably not something I'll do in the near future.

  • I know about such tricks as "Rez.Strings[symbol]" helper function and "var strings = Rez.Strings; var str1 = strings.SomeString; var str2 = strings.SomeOtherString;" but they all lack compile time checks to ensure SomeString exists

    I thought the "var strings = Rez.Strings" case would be correctly type checked by my optimizer. And it does have all the necessary information. eg

    var mSymbol as Symbol; // or ResourceId with sdk-7.x
    
    function test() {
      var strings = Rez.Strings;
      mSymbol = strings.foobar; // reported as a type error
      mSymbol = strings.AppName; // fine
    }

    But it doesn't report missing symbols directly. It turns out that I wrote a separate pass to find missing symbols prior to writing the type checker, and it still does all its checking without the use of types... I think this is something I can easily fix.

    Of course the downside is that Garmin's type checker has to be disabled on any function that uses this trick...

    The other thing that probably isn't too hard is to upgrade the post build optimizer's PRE pass to do this rewrite for you. It already improves things a little, but it currently doesn't have enough information about the module structure to get to the optimal solution. I'd already started looking at fixing PRE to be optimal given all the new instructions for system 7 devices, so I'll take a look at what's involved.

  • Probably a really tall ask, but the profiler either shows toybox symbols and hex app addresses, or toybox hex addresses and app symbols if you load the app debug xml. Is there anyway to combine them so when I get the PRG files from the watch I can see all the symbols? Or, are the addresses for the symbols in the watch different from the simulator addresses and thus just not known?

  • First off, this sounds like a bug that Garmin should probably address, but

    Is there anyway to combine them so when I get the PRG files from the watch I can see all the symbols

    It looks like you can just combine like sections - everything from <pctoLineNum> from both files, etc.

    Or, are the addresses for the symbols in the watch different from the simulator addresses and thus just not known

    I don't know for sure, but the fact that they provide a debug.xml for each device suggests that they've bothered to get the addresses right...

  • this sounds like a bug that Garmin should probably address

    I agree, but in just responding you've already given it more time than I think they ever will. Thanks.

    I did try to merge the files myself, but either it's not possible or I did it wrong (probably). Not knowing the guts of the optimizer I don't know if the necessary extents of the symbol table are available to make such a thing or not so I figured I'd start by asking someone who might know and most likely would respond.

  • v2.0.90 is out.

    This has an important fix. Garmin has informed me that their app store will start rejecting apps that have had the argc bytecodes removed. This version turns off the optimization, and should issue deprecation notices if you're using the option. I don't know for sure, but I think this check only happens on upload, so any apps that have already been uploaded should be fine.

    It also fixes #14

  • Mark - I've been affected by this issue. See: https://forums.garmin.com/developer/connect-iq/f/discussion/371201/app-updates-blocked-due-to-security-threat

    Seems like there might also be something in the Post Build Optimizer that Garmin doesn't like either. Once I disabled this, then I could upload my app again - that said, it now crashes on Fenix 6 due to out of memory issues which is why I was using the optimizer in the first place :-(